| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.hcgallery; |
| |
| import android.app.ActionBar; |
| import android.app.Activity; |
| import android.app.FragmentTransaction; |
| import android.app.ListFragment; |
| import android.content.ClipData; |
| import android.content.res.TypedArray; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.view.View; |
| import android.view.ViewTreeObserver; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemLongClickListener; |
| import android.widget.ArrayAdapter; |
| import android.widget.FrameLayout; |
| import android.widget.FrameLayout.LayoutParams; |
| import android.widget.ListView; |
| import android.widget.TextView; |
| |
| /** |
| * Fragment that shows the list of images |
| * As an extension of ListFragment, this fragment uses a default layout |
| * that includes a single ListView, which you can acquire with getListView() |
| * When running on a screen size smaller than "large", this fragment appears alone |
| * in MainActivity. In this case, selecting a list item opens the ContentActivity, |
| * which likewise holds only the ContentFragment. |
| */ |
| public class TitlesFragment extends ListFragment implements ActionBar.TabListener { |
| OnItemSelectedListener mListener; |
| private int mCategory = 0; |
| private int mCurPosition = 0; |
| private boolean mDualFragments = false; |
| |
| /** Container Activity must implement this interface and we ensure |
| * that it does during the onAttach() callback |
| */ |
| public interface OnItemSelectedListener { |
| public void onItemSelected(int category, int position); |
| } |
| |
| @Override |
| public void onAttach(Activity activity) { |
| super.onAttach(activity); |
| // Check that the container activity has implemented the callback interface |
| try { |
| mListener = (OnItemSelectedListener) activity; |
| } catch (ClassCastException e) { |
| throw new ClassCastException(activity.toString() |
| + " must implement OnItemSelectedListener"); |
| } |
| } |
| |
| /** This is where we perform setup for the fragment that's either |
| * not related to the fragment's layout or must be done after the layout is drawn. |
| * Notice that this fragment does not implement onCreateView(), because it extends |
| * ListFragment, which includes a ListView as the root view by default, so there's |
| * no need to set up the layout. |
| */ |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| super.onActivityCreated(savedInstanceState); |
| |
| ContentFragment frag = (ContentFragment) getFragmentManager() |
| .findFragmentById(R.id.content_frag); |
| if (frag != null) mDualFragments = true; |
| |
| ActionBar bar = getActivity().getActionBar(); |
| bar.setDisplayHomeAsUpEnabled(false); |
| bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); |
| |
| // Must call in order to get callback to onCreateOptionsMenu() |
| setHasOptionsMenu(true); |
| |
| Directory.initializeDirectory(); |
| for (int i = 0; i < Directory.getCategoryCount(); i++) { |
| bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName()) |
| .setTabListener(this)); |
| } |
| |
| //Current position should survive screen rotations. |
| if (savedInstanceState != null) { |
| mCategory = savedInstanceState.getInt("category"); |
| mCurPosition = savedInstanceState.getInt("listPosition"); |
| bar.selectTab(bar.getTabAt(mCategory)); |
| } |
| |
| populateTitles(mCategory); |
| ListView lv = getListView(); |
| lv.setCacheColorHint(Color.TRANSPARENT); // Improves scrolling performance |
| |
| if (mDualFragments) { |
| // Highlight the currently selected item |
| lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
| // Enable drag and dropping |
| lv.setOnItemLongClickListener(new OnItemLongClickListener() { |
| public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) { |
| final String title = (String) ((TextView) v).getText(); |
| |
| // Set up clip data with the category||entry_id format. |
| final String textData = String.format("%d||%d", mCategory, pos); |
| ClipData data = ClipData.newPlainText(title, textData); |
| v.startDrag(data, new MyDragShadowBuilder(v), null, 0); |
| return true; |
| } |
| }); |
| } |
| |
| // If showing both fragments, select the appropriate list item by default |
| if (mDualFragments) selectPosition(mCurPosition); |
| |
| // Attach a GlobalLayoutListener so that we get a callback when the layout |
| // has finished drawing. This is necessary so that we can apply top-margin |
| // to the ListView in order to dodge the ActionBar. Ordinarily, that's not |
| // necessary, but we've set the ActionBar to "overlay" mode using our theme, |
| // so the layout does not account for the action bar position on its own. |
| ViewTreeObserver observer = getListView().getViewTreeObserver(); |
| observer.addOnGlobalLayoutListener(layoutListener); |
| } |
| |
| @Override |
| public void onDestroyView() { |
| super.onDestroyView(); |
| // Always detach ViewTreeObserver listeners when the view tears down |
| getListView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener); |
| } |
| |
| /** Attaches an adapter to the fragment's ListView to populate it with items */ |
| public void populateTitles(int category) { |
| DirectoryCategory cat = Directory.getCategory(category); |
| String[] items = new String[cat.getEntryCount()]; |
| for (int i = 0; i < cat.getEntryCount(); i++) |
| items[i] = cat.getEntry(i).getName(); |
| // Convenience method to attach an adapter to ListFragment's ListView |
| setListAdapter(new ArrayAdapter<String>(getActivity(), |
| R.layout.title_list_item, items)); |
| mCategory = category; |
| } |
| |
| @Override |
| public void onListItemClick(ListView l, View v, int position, long id) { |
| // Send the event to the host activity via OnItemSelectedListener callback |
| mListener.onItemSelected(mCategory, position); |
| mCurPosition = position; |
| } |
| |
| /** Called to select an item from the listview */ |
| public void selectPosition(int position) { |
| // Only if we're showing both fragments should the item be "highlighted" |
| if (mDualFragments) { |
| ListView lv = getListView(); |
| lv.setItemChecked(position, true); |
| } |
| // Calls the parent activity's implementation of the OnItemSelectedListener |
| // so the activity can pass the event to the sibling fragment as appropriate |
| mListener.onItemSelected(mCategory, position); |
| } |
| |
| @Override |
| public void onSaveInstanceState (Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putInt("listPosition", mCurPosition); |
| outState.putInt("category", mCategory); |
| } |
| |
| /** This defines how the draggable list items appear during a drag event */ |
| private class MyDragShadowBuilder extends View.DragShadowBuilder { |
| private Drawable mShadow; |
| |
| public MyDragShadowBuilder(View v) { |
| super(v); |
| |
| final TypedArray a = v.getContext().obtainStyledAttributes(R.styleable.AppTheme); |
| mShadow = a.getDrawable(R.styleable.AppTheme_listDragShadowBackground); |
| mShadow.setCallback(v); |
| mShadow.setBounds(0, 0, v.getWidth(), v.getHeight()); |
| a.recycle(); |
| } |
| |
| @Override |
| public void onDrawShadow(Canvas canvas) { |
| super.onDrawShadow(canvas); |
| mShadow.draw(canvas); |
| getView().draw(canvas); |
| } |
| } |
| |
| // Because the fragment doesn't have a reliable callback to notify us when |
| // the activity's layout is completely drawn, this OnGlobalLayoutListener provides |
| // the necessary callback so we can add top-margin to the ListView in order to dodge |
| // the ActionBar. Which is necessary because the ActionBar is in overlay mode, meaning |
| // that it will ordinarily sit on top of the activity layout as a top layer and |
| // the ActionBar height can vary. Specifically, when on a small/normal size screen, |
| // the action bar tabs appear in a second row, making the action bar twice as tall. |
| ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { |
| @Override |
| public void onGlobalLayout() { |
| int barHeight = getActivity().getActionBar().getHeight(); |
| ListView listView = getListView(); |
| FrameLayout.LayoutParams params = (LayoutParams) listView.getLayoutParams(); |
| // The list view top-margin should always match the action bar height |
| if (params.topMargin != barHeight) { |
| params.topMargin = barHeight; |
| listView.setLayoutParams(params); |
| } |
| // The action bar doesn't update its height when hidden, so make top-margin zero |
| if (!getActivity().getActionBar().isShowing()) { |
| params.topMargin = 0; |
| listView.setLayoutParams(params); |
| } |
| } |
| }; |
| |
| |
| /* The following are callbacks implemented for the ActionBar.TabListener, |
| * which this fragment implements to handle events when tabs are selected. |
| */ |
| |
| public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { |
| TitlesFragment titleFrag = (TitlesFragment) getFragmentManager() |
| .findFragmentById(R.id.titles_frag); |
| titleFrag.populateTitles(tab.getPosition()); |
| |
| if (mDualFragments) { |
| titleFrag.selectPosition(0); |
| } |
| } |
| |
| /* These must be implemented, but we don't use them */ |
| |
| public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { |
| } |
| |
| public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { |
| } |
| |
| } |