diff options
| author | Dianne Hackborn <hackbod@google.com> | 2010-06-12 10:15:28 -0700 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2010-06-12 22:34:04 -0700 |
| commit | 5ddd127d5a38d80c0d8087d1770f41f61f84f048 (patch) | |
| tree | 0992c5376b76434d65aaeee93a054822f0582dbc /core/java/android/app | |
| parent | 4dc643eb6749afe5211198c920e739475590f92c (diff) | |
| download | frameworks_base-5ddd127d5a38d80c0d8087d1770f41f61f84f048.zip frameworks_base-5ddd127d5a38d80c0d8087d1770f41f61f84f048.tar.gz frameworks_base-5ddd127d5a38d80c0d8087d1770f41f61f84f048.tar.bz2 | |
Implement ListFragment and Fragment context menus.
Also fix the docs build.
Change-Id: I96f24cc03e6debd897171e503957284b140a9f27
Diffstat (limited to 'core/java/android/app')
| -rw-r--r-- | core/java/android/app/Activity.java | 5 | ||||
| -rw-r--r-- | core/java/android/app/Fragment.java | 94 | ||||
| -rw-r--r-- | core/java/android/app/FragmentManager.java | 20 | ||||
| -rw-r--r-- | core/java/android/app/ListFragment.java | 280 |
4 files changed, 394 insertions, 5 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 98505c5..ed37058 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2296,7 +2296,10 @@ public class Activity extends ContextThemeWrapper case Window.FEATURE_CONTEXT_MENU: EventLog.writeEvent(50000, 1, item.getTitleCondensed()); - return onContextItemSelected(item); + if (onContextItemSelected(item)) { + return true; + } + return mFragments.dispatchContextItemSelected(item); default: return false; diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 591b7a2..231d6ab 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -24,13 +24,17 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.SparseArray; +import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View.OnCreateContextMenuListener; import android.view.animation.Animation; +import android.widget.AdapterView; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; @@ -129,7 +133,7 @@ final class FragmentState implements Parcelable { * A Fragment is a piece of an application's user interface or behavior * that can be placed in an {@link Activity}. */ -public class Fragment implements ComponentCallbacks { +public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener { private static final HashMap<String, Class> sClassMap = new HashMap<String, Class>(); @@ -456,6 +460,9 @@ public class Fragment implements ComponentCallbacks { * is the default implementation). This will be called between * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}. * + * <p>If you return a View from here, you will later be called in + * {@link #onDestroyView} when the view is being released. + * * @param inflater The LayoutInflater object that can be used to inflate * any views in the fragment, * @param container If non-null, this is the parent view that the fragment's @@ -479,7 +486,7 @@ public class Fragment implements ComponentCallbacks { * Called when the activity is ready for the fragment to run. This is * most useful for fragments that use {@link #setRetainInstance(boolean)} * instance, as this tells the fragment when it is fully associated with - * the new activity instance. This is called after {@link #onCreate(Bundle)} + * the new activity instance. This is called after {@link #onCreateView} * and before {@link #onStart()}. * * @param savedInstanceState If the fragment is being re-created from @@ -538,6 +545,17 @@ public class Fragment implements ComponentCallbacks { } /** + * Called when the view previously created by {@link #onCreateView} has + * been detached from the fragment. The next time the fragment needs + * to be displayed, a new view will be created. This is called + * after {@link #onStop()} and before {@link #onDestroy()}; it is only + * called if {@link #onCreateView} returns a non-null View. + */ + public void onDestroyView() { + mCalled = true; + } + + /** * Called when the fragment is no longer in use. This is called * after {@link #onStop()} and before {@link #onDetach()}. */ @@ -556,8 +574,9 @@ public class Fragment implements ComponentCallbacks { /** * Initialize the contents of the Activity's standard options menu. You * should place your menu items in to <var>menu</var>. For this method - * to be called, you must have first called {@link #setHasOptionsMenu}. - * See {@link Activity#onCreateOptionsMenu} for more information. + * to be called, you must have first called {@link #setHasOptionsMenu}. See + * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} + * for more information. * * @param menu The options menu in which you place your items. * @@ -616,4 +635,71 @@ public class Fragment implements ComponentCallbacks { */ public void onOptionsMenuClosed(Menu menu) { } + + /** + * Called when a context menu for the {@code view} is about to be shown. + * Unlike {@link #onCreateOptionsMenu}, this will be called every + * time the context menu is about to be shown and should be populated for + * the view (or item inside the view for {@link AdapterView} subclasses, + * this can be found in the {@code menuInfo})). + * <p> + * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an + * item has been selected. + * <p> + * The default implementation calls up to + * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though + * you can not call this implementation if you don't want that behavior. + * <p> + * It is not safe to hold onto the context menu after this method returns. + * {@inheritDoc} + */ + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + getActivity().onCreateContextMenu(menu, v, menuInfo); + } + + /** + * Registers a context menu to be shown for the given view (multiple views + * can show the context menu). This method will set the + * {@link OnCreateContextMenuListener} on the view to this fragment, so + * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be + * called when it is time to show the context menu. + * + * @see #unregisterForContextMenu(View) + * @param view The view that should show a context menu. + */ + public void registerForContextMenu(View view) { + view.setOnCreateContextMenuListener(this); + } + + /** + * Prevents a context menu to be shown for the given view. This method will + * remove the {@link OnCreateContextMenuListener} on the view. + * + * @see #registerForContextMenu(View) + * @param view The view that should stop showing a context menu. + */ + public void unregisterForContextMenu(View view) { + view.setOnCreateContextMenuListener(null); + } + + /** + * This hook is called whenever an item in a context menu is selected. The + * default implementation simply returns false to have the normal processing + * happen (calling the item's Runnable or sending a message to its Handler + * as appropriate). You can use this method for any items for which you + * would like to do processing without those other facilities. + * <p> + * Use {@link MenuItem#getMenuInfo()} to get extra information set by the + * View that added this menu item. + * <p> + * Derived classes should call through to the base class for it to perform + * the default menu handling. + * + * @param item The context menu item that was selected. + * @return boolean Return false to allow normal context menu processing to + * proceed, true to consume it here. + */ + public boolean onContextItemSelected(MenuItem item) { + return false; + } } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index b837c32..c0ab0b5 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -259,6 +259,12 @@ public class FragmentManager { if (newState < Fragment.CONTENT) { if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); if (f.mView != null) { + f.mCalled = false; + f.onDestroyView(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onDestroyedView()"); + } // Need to save the current view state if not // done already. if (!mActivity.isFinishing() && f.mSavedFragmentState == null) { @@ -781,6 +787,20 @@ public class FragmentManager { return false; } + public boolean dispatchContextItemSelected(MenuItem item) { + if (mActive != null) { + for (int i=0; i<mAdded.size(); i++) { + Fragment f = mAdded.get(i); + if (f != null && !f.mHidden) { + if (f.onContextItemSelected(item)) { + return true; + } + } + } + } + return false; + } + public void dispatchOptionsMenuClosed(Menu menu) { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java new file mode 100644 index 0000000..0d8ec98 --- /dev/null +++ b/core/java/android/app/ListFragment.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010 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 android.app; + +import android.os.Bundle; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; + +/** + * An fragment that displays a list of items by binding to a data source such as + * an array or Cursor, and exposes event handlers when the user selects an item. + * <p> + * ListActivity hosts a {@link android.widget.ListView ListView} object that can + * be bound to different data sources, typically either an array or a Cursor + * holding query results. Binding, screen layout, and row layout are discussed + * in the following sections. + * <p> + * <strong>Screen Layout</strong> + * </p> + * <p> + * ListActivity has a default layout that consists of a single list view. + * However, if you desire, you can customize the fragment layout by returning + * your own view hierarchy from {@link #onCreateView}. + * To do this, your view hierarchy MUST contain a ListView object with the + * id "@android:id/list" (or {@link android.R.id#list} if it's in code) + * <p> + * Optionally, your view hierarchy can contain another view object of any type to + * display when the list view is empty. This "empty list" notifier must have an + * id "android:empty". Note that when an empty view is present, the list view + * will be hidden when there is no data to display. + * <p> + * The following code demonstrates an (ugly) custom lisy layout. It has a list + * with a green background, and an alternate red "no data" message. + * </p> + * + * <pre> + * <?xml version="1.0" encoding="utf-8"?> + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * android:orientation="vertical" + * android:layout_width="match_parent" + * android:layout_height="match_parent" + * android:paddingLeft="8dp" + * android:paddingRight="8dp"> + * + * <ListView android:id="@id/android:list" + * android:layout_width="match_parent" + * android:layout_height="match_parent" + * android:background="#00FF00" + * android:layout_weight="1" + * android:drawSelectorOnTop="false"/> + * + * <TextView android:id="@id/android:empty" + * android:layout_width="match_parent" + * android:layout_height="match_parent" + * android:background="#FF0000" + * android:text="No data"/> + * </LinearLayout> + * </pre> + * + * <p> + * <strong>Row Layout</strong> + * </p> + * <p> + * You can specify the layout of individual rows in the list. You do this by + * specifying a layout resource in the ListAdapter object hosted by the fragment + * (the ListAdapter binds the ListView to the data; more on this later). + * <p> + * A ListAdapter constructor takes a parameter that specifies a layout resource + * for each row. It also has two additional parameters that let you specify + * which data field to associate with which object in the row layout resource. + * These two parameters are typically parallel arrays. + * </p> + * <p> + * Android provides some standard row layout resources. These are in the + * {@link android.R.layout} class, and have names such as simple_list_item_1, + * simple_list_item_2, and two_line_list_item. The following layout XML is the + * source for the resource two_line_list_item, which displays two data + * fields,one above the other, for each list row. + * </p> + * + * <pre> + * <?xml version="1.0" encoding="utf-8"?> + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * android:layout_width="match_parent" + * android:layout_height="wrap_content" + * android:orientation="vertical"> + * + * <TextView android:id="@+id/text1" + * android:textSize="16sp" + * android:textStyle="bold" + * android:layout_width="match_parent" + * android:layout_height="wrap_content"/> + * + * <TextView android:id="@+id/text2" + * android:textSize="16sp" + * android:layout_width="match_parent" + * android:layout_height="wrap_content"/> + * </LinearLayout> + * </pre> + * + * <p> + * You must identify the data bound to each TextView object in this layout. The + * syntax for this is discussed in the next section. + * </p> + * <p> + * <strong>Binding to Data</strong> + * </p> + * <p> + * You bind the ListFragment's ListView object to data using a class that + * implements the {@link android.widget.ListAdapter ListAdapter} interface. + * Android provides two standard list adapters: + * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps), + * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor + * query results. + * </p> + * + * @see #setListAdapter + * @see android.widget.ListView + */ +public class ListFragment extends Fragment { + final private Handler mHandler = new Handler(); + + final private Runnable mRequestFocus = new Runnable() { + public void run() { + mList.focusableViewAvailable(mList); + } + }; + + final private AdapterView.OnItemClickListener mOnClickListener + = new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView<?> parent, View v, int position, long id) { + onListItemClick((ListView)parent, v, position, id); + } + }; + + ListAdapter mAdapter; + ListView mList; + + public ListFragment() { + } + + /** + * Provide default implementation to return a simple list view. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(com.android.internal.R.layout.list_content, + container, false); + } + + /** + * Attach to list view once Fragment is ready to run. + */ + @Override + public void onReady(Bundle savedInstanceState) { + super.onReady(savedInstanceState); + ensureList(); + } + + /** + * Detach from list view. + */ + @Override + public void onDestroyView() { + mHandler.removeCallbacks(mRequestFocus); + mList = null; + super.onDestroyView(); + } + + /** + * This method will be called when an item in the list is selected. + * Subclasses should override. Subclasses can call + * getListView().getItemAtPosition(position) if they need to access the + * data associated with the selected item. + * + * @param l The ListView where the click happened + * @param v The view that was clicked within the ListView + * @param position The position of the view in the list + * @param id The row id of the item that was clicked + */ + public void onListItemClick(ListView l, View v, int position, long id) { + } + + /** + * Provide the cursor for the list view. + */ + public void setListAdapter(ListAdapter adapter) { + mAdapter = adapter; + if (mList != null) { + mList.setAdapter(adapter); + } + } + + /** + * Set the currently selected list item to the specified + * position with the adapter's data + * + * @param position + */ + public void setSelection(int position) { + ensureList(); + mList.setSelection(position); + } + + /** + * Get the position of the currently selected list item. + */ + public int getSelectedItemPosition() { + ensureList(); + return mList.getSelectedItemPosition(); + } + + /** + * Get the cursor row ID of the currently selected list item. + */ + public long getSelectedItemId() { + ensureList(); + return mList.getSelectedItemId(); + } + + /** + * Get the activity's list view widget. + */ + public ListView getListView() { + ensureList(); + return mList; + } + + /** + * Get the ListAdapter associated with this activity's ListView. + */ + public ListAdapter getListAdapter() { + return mAdapter; + } + + private void ensureList() { + if (mList != null) { + return; + } + View root = getView(); + if (root == null) { + throw new IllegalStateException("Content view not yet created"); + } + View emptyView = root.findViewById(com.android.internal.R.id.empty); + mList = (ListView)root.findViewById(com.android.internal.R.id.list); + if (mList == null) { + throw new RuntimeException( + "Your content must have a ListView whose id attribute is " + + "'android.R.id.list'"); + } + if (emptyView != null) { + mList.setEmptyView(emptyView); + } + mList.setOnItemClickListener(mOnClickListener); + if (mAdapter != null) { + setListAdapter(mAdapter); + } + mHandler.post(mRequestFocus); + } +} |
