Home | History | Annotate | Download | only in hcgallery
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.hcgallery;
     18 
     19 import android.app.ActionBar;
     20 import android.app.Activity;
     21 import android.app.FragmentTransaction;
     22 import android.app.ListFragment;
     23 import android.content.ClipData;
     24 import android.content.res.TypedArray;
     25 import android.graphics.Canvas;
     26 import android.graphics.Color;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Bundle;
     29 import android.view.View;
     30 import android.view.ViewTreeObserver;
     31 import android.widget.AdapterView;
     32 import android.widget.AdapterView.OnItemLongClickListener;
     33 import android.widget.ArrayAdapter;
     34 import android.widget.FrameLayout;
     35 import android.widget.FrameLayout.LayoutParams;
     36 import android.widget.ListView;
     37 import android.widget.TextView;
     38 
     39 /**
     40  * Fragment that shows the list of images
     41  * As an extension of ListFragment, this fragment uses a default layout
     42  * that includes a single ListView, which you can acquire with getListView()
     43  * When running on a screen size smaller than "large", this fragment appears alone
     44  * in MainActivity. In this case, selecting a list item opens the ContentActivity,
     45  * which likewise holds only the ContentFragment.
     46  */
     47 public class TitlesFragment extends ListFragment implements ActionBar.TabListener {
     48     OnItemSelectedListener mListener;
     49     private int mCategory = 0;
     50     private int mCurPosition = 0;
     51     private boolean mDualFragments = false;
     52 
     53     /** Container Activity must implement this interface and we ensure
     54      * that it does during the onAttach() callback
     55      */
     56     public interface OnItemSelectedListener {
     57         public void onItemSelected(int category, int position);
     58     }
     59 
     60     @Override
     61     public void onAttach(Activity activity) {
     62         super.onAttach(activity);
     63         // Check that the container activity has implemented the callback interface
     64         try {
     65             mListener = (OnItemSelectedListener) activity;
     66         } catch (ClassCastException e) {
     67             throw new ClassCastException(activity.toString()
     68                     + " must implement OnItemSelectedListener");
     69         }
     70     }
     71 
     72     /** This is where we perform setup for the fragment that's either
     73      * not related to the fragment's layout or must be done after the layout is drawn.
     74      * Notice that this fragment does not implement onCreateView(), because it extends
     75      * ListFragment, which includes a ListView as the root view by default, so there's
     76      * no need to set up the layout.
     77      */
     78     @Override
     79     public void onActivityCreated(Bundle savedInstanceState) {
     80         super.onActivityCreated(savedInstanceState);
     81 
     82         ContentFragment frag = (ContentFragment) getFragmentManager()
     83                 .findFragmentById(R.id.content_frag);
     84         if (frag != null) mDualFragments = true;
     85 
     86         ActionBar bar = getActivity().getActionBar();
     87         bar.setDisplayHomeAsUpEnabled(false);
     88         bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
     89 
     90         // Must call in order to get callback to onCreateOptionsMenu()
     91         setHasOptionsMenu(true);
     92 
     93         Directory.initializeDirectory();
     94         for (int i = 0; i < Directory.getCategoryCount(); i++) {
     95             bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
     96                     .setTabListener(this));
     97         }
     98 
     99         //Current position should survive screen rotations.
    100         if (savedInstanceState != null) {
    101             mCategory = savedInstanceState.getInt("category");
    102             mCurPosition = savedInstanceState.getInt("listPosition");
    103             bar.selectTab(bar.getTabAt(mCategory));
    104         }
    105 
    106         populateTitles(mCategory);
    107         ListView lv = getListView();
    108         lv.setCacheColorHint(Color.TRANSPARENT); // Improves scrolling performance
    109 
    110         if (mDualFragments) {
    111             // Highlight the currently selected item
    112             lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    113             // Enable drag and dropping
    114             lv.setOnItemLongClickListener(new OnItemLongClickListener() {
    115                 public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
    116                     final String title = (String) ((TextView) v).getText();
    117 
    118                     // Set up clip data with the category||entry_id format.
    119                     final String textData = String.format("%d||%d", mCategory, pos);
    120                     ClipData data = ClipData.newPlainText(title, textData);
    121                     v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
    122                     return true;
    123                 }
    124             });
    125         }
    126 
    127         // If showing both fragments, select the appropriate list item by default
    128         if (mDualFragments) selectPosition(mCurPosition);
    129 
    130         // Attach a GlobalLayoutListener so that we get a callback when the layout
    131         // has finished drawing. This is necessary so that we can apply top-margin
    132         // to the ListView in order to dodge the ActionBar. Ordinarily, that's not
    133         // necessary, but we've set the ActionBar to "overlay" mode using our theme,
    134         // so the layout does not account for the action bar position on its own.
    135         ViewTreeObserver observer = getListView().getViewTreeObserver();
    136         observer.addOnGlobalLayoutListener(layoutListener);
    137     }
    138 
    139     @Override
    140     public void onDestroyView() {
    141       super.onDestroyView();
    142       // Always detach ViewTreeObserver listeners when the view tears down
    143       getListView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener);
    144     }
    145 
    146     /** Attaches an adapter to the fragment's ListView to populate it with items */
    147     public void populateTitles(int category) {
    148         DirectoryCategory cat = Directory.getCategory(category);
    149         String[] items = new String[cat.getEntryCount()];
    150         for (int i = 0; i < cat.getEntryCount(); i++)
    151             items[i] = cat.getEntry(i).getName();
    152         // Convenience method to attach an adapter to ListFragment's ListView
    153         setListAdapter(new ArrayAdapter<String>(getActivity(),
    154                 R.layout.title_list_item, items));
    155         mCategory = category;
    156     }
    157 
    158     @Override
    159     public void onListItemClick(ListView l, View v, int position, long id) {
    160         // Send the event to the host activity via OnItemSelectedListener callback
    161         mListener.onItemSelected(mCategory, position);
    162         mCurPosition = position;
    163     }
    164 
    165     /** Called to select an item from the listview */
    166     public void selectPosition(int position) {
    167         // Only if we're showing both fragments should the item be "highlighted"
    168         if (mDualFragments) {
    169             ListView lv = getListView();
    170             lv.setItemChecked(position, true);
    171         }
    172         // Calls the parent activity's implementation of the OnItemSelectedListener
    173         // so the activity can pass the event to the sibling fragment as appropriate
    174         mListener.onItemSelected(mCategory, position);
    175     }
    176 
    177     @Override
    178     public void onSaveInstanceState (Bundle outState) {
    179         super.onSaveInstanceState(outState);
    180         outState.putInt("listPosition", mCurPosition);
    181         outState.putInt("category", mCategory);
    182     }
    183 
    184     /** This defines how the draggable list items appear during a drag event */
    185     private class MyDragShadowBuilder extends View.DragShadowBuilder {
    186         private Drawable mShadow;
    187 
    188         public MyDragShadowBuilder(View v) {
    189             super(v);
    190 
    191             final TypedArray a = v.getContext().obtainStyledAttributes(R.styleable.AppTheme);
    192             mShadow = a.getDrawable(R.styleable.AppTheme_listDragShadowBackground);
    193             mShadow.setCallback(v);
    194             mShadow.setBounds(0, 0, v.getWidth(), v.getHeight());
    195             a.recycle();
    196         }
    197 
    198         @Override
    199         public void onDrawShadow(Canvas canvas) {
    200             super.onDrawShadow(canvas);
    201             mShadow.draw(canvas);
    202             getView().draw(canvas);
    203         }
    204     }
    205 
    206     // Because the fragment doesn't have a reliable callback to notify us when
    207     // the activity's layout is completely drawn, this OnGlobalLayoutListener provides
    208     // the necessary callback so we can add top-margin to the ListView in order to dodge
    209     // the ActionBar. Which is necessary because the ActionBar is in overlay mode, meaning
    210     // that it will ordinarily sit on top of the activity layout as a top layer and
    211     // the ActionBar height can vary. Specifically, when on a small/normal size screen,
    212     // the action bar tabs appear in a second row, making the action bar twice as tall.
    213     ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    214         @Override
    215         public void onGlobalLayout() {
    216             int barHeight = getActivity().getActionBar().getHeight();
    217             ListView listView = getListView();
    218             FrameLayout.LayoutParams params = (LayoutParams) listView.getLayoutParams();
    219             // The list view top-margin should always match the action bar height
    220             if (params.topMargin != barHeight) {
    221                 params.topMargin = barHeight;
    222                 listView.setLayoutParams(params);
    223             }
    224             // The action bar doesn't update its height when hidden, so make top-margin zero
    225             if (!getActivity().getActionBar().isShowing()) {
    226               params.topMargin = 0;
    227               listView.setLayoutParams(params);
    228             }
    229         }
    230     };
    231 
    232 
    233     /* The following are callbacks implemented for the ActionBar.TabListener,
    234      * which this fragment implements to handle events when tabs are selected.
    235      */
    236 
    237     public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
    238         TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
    239                 .findFragmentById(R.id.titles_frag);
    240         titleFrag.populateTitles(tab.getPosition());
    241 
    242         if (mDualFragments) {
    243             titleFrag.selectPosition(0);
    244         }
    245     }
    246 
    247     /* These must be implemented, but we don't use them */
    248 
    249     public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
    250     }
    251 
    252     public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
    253     }
    254 
    255 }
    256