ListActivity.java revision f767e7521257110e5ae4289ded42e1deeb972e16
1/*
2 * Copyright (C) 2006 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
17package android.app;
18
19import android.os.Bundle;
20import android.os.Handler;
21import android.view.KeyEvent;
22import android.view.View;
23import android.widget.Adapter;
24import android.widget.AdapterView;
25import android.widget.ListAdapter;
26import android.widget.ListView;
27
28/**
29 * An activity that displays a list of items by binding to a data source such as
30 * an array or Cursor, and exposes event handlers when the user selects an item.
31 * <p>
32 * ListActivity hosts a {@link android.widget.ListView ListView} object that can
33 * be bound to different data sources, typically either an array or a Cursor
34 * holding query results. Binding, screen layout, and row layout are discussed
35 * in the following sections.
36 * <p>
37 * <strong>Screen Layout</strong>
38 * </p>
39 * <p>
40 * ListActivity has a default layout that consists of a single, full-screen list
41 * in the center of the screen. However, if you desire, you can customize the
42 * screen layout by setting your own view layout with setContentView() in
43 * onCreate(). To do this, your own view MUST contain a ListView object with the
44 * id "@android:id/list" (or {@link android.R.id#list} if it's in code)
45 * <p>
46 * Optionally, your custom view can contain another view object of any type to
47 * display when the list view is empty. This "empty list" notifier must have an
48 * id "android:empty". Note that when an empty view is present, the list view
49 * will be hidden when there is no data to display.
50 * <p>
51 * The following code demonstrates an (ugly) custom screen layout. It has a list
52 * with a green background, and an alternate red "no data" message.
53 * </p>
54 *
55 * <pre>
56 * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
57 * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
58 *         android:orientation=&quot;vertical&quot;
59 *         android:layout_width=&quot;match_parent&quot;
60 *         android:layout_height=&quot;match_parent&quot;
61 *         android:paddingLeft=&quot;8dp&quot;
62 *         android:paddingRight=&quot;8dp&quot;&gt;
63 *
64 *     &lt;ListView android:id=&quot;@id/android:list&quot;
65 *               android:layout_width=&quot;match_parent&quot;
66 *               android:layout_height=&quot;match_parent&quot;
67 *               android:background=&quot;#00FF00&quot;
68 *               android:layout_weight=&quot;1&quot;
69 *               android:drawSelectorOnTop=&quot;false&quot;/&gt;
70 *
71 *     &lt;TextView id=&quot;@id/android:empty&quot;
72 *               android:layout_width=&quot;match_parent&quot;
73 *               android:layout_height=&quot;match_parent&quot;
74 *               android:background=&quot;#FF0000&quot;
75 *               android:text=&quot;No data&quot;/&gt;
76 * &lt;/LinearLayout&gt;
77 * </pre>
78 *
79 * <p>
80 * <strong>Row Layout</strong>
81 * </p>
82 * <p>
83 * You can specify the layout of individual rows in the list. You do this by
84 * specifying a layout resource in the ListAdapter object hosted by the activity
85 * (the ListAdapter binds the ListView to the data; more on this later).
86 * <p>
87 * A ListAdapter constructor takes a parameter that specifies a layout resource
88 * for each row. It also has two additional parameters that let you specify
89 * which data field to associate with which object in the row layout resource.
90 * These two parameters are typically parallel arrays.
91 * </p>
92 * <p>
93 * Android provides some standard row layout resources. These are in the
94 * {@link android.R.layout} class, and have names such as simple_list_item_1,
95 * simple_list_item_2, and two_line_list_item. The following layout XML is the
96 * source for the resource two_line_list_item, which displays two data
97 * fields,one above the other, for each list row.
98 * </p>
99 *
100 * <pre>
101 * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
102 * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
103 *     android:layout_width=&quot;match_parent&quot;
104 *     android:layout_height=&quot;wrap_content&quot;
105 *     android:orientation=&quot;vertical&quot;&gt;
106 *
107 *     &lt;TextView android:id=&quot;@+id/text1&quot;
108 *         android:textSize=&quot;16sp&quot;
109 *         android:textStyle=&quot;bold&quot;
110 *         android:layout_width=&quot;match_parent&quot;
111 *         android:layout_height=&quot;wrap_content&quot;/&gt;
112 *
113 *     &lt;TextView android:id=&quot;@+id/text2&quot;
114 *         android:textSize=&quot;16sp&quot;
115 *         android:layout_width=&quot;match_parent&quot;
116 *         android:layout_height=&quot;wrap_content&quot;/&gt;
117 * &lt;/LinearLayout&gt;
118 * </pre>
119 *
120 * <p>
121 * You must identify the data bound to each TextView object in this layout. The
122 * syntax for this is discussed in the next section.
123 * </p>
124 * <p>
125 * <strong>Binding to Data</strong>
126 * </p>
127 * <p>
128 * You bind the ListActivity's ListView object to data using a class that
129 * implements the {@link android.widget.ListAdapter ListAdapter} interface.
130 * Android provides two standard list adapters:
131 * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps),
132 * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor
133 * query results.
134 * </p>
135 * <p>
136 * The following code from a custom ListActivity demonstrates querying the
137 * Contacts provider for all contacts, then binding the Name and Company fields
138 * to a two line row layout in the activity's ListView.
139 * </p>
140 *
141 * <pre>
142 * public class MyListAdapter extends ListActivity {
143 *
144 *     &#064;Override
145 *     protected void onCreate(Bundle savedInstanceState){
146 *         super.onCreate(savedInstanceState);
147 *
148 *         // We'll define a custom screen layout here (the one shown above), but
149 *         // typically, you could just use the standard ListActivity layout.
150 *         setContentView(R.layout.custom_list_activity_view);
151 *
152 *         // Query for all people contacts using the {@link android.provider.Contacts.People} convenience class.
153 *         // Put a managed wrapper around the retrieved cursor so we don't have to worry about
154 *         // requerying or closing it as the activity changes state.
155 *         mCursor = this.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
156 *         startManagingCursor(mCursor);
157 *
158 *         // Now create a new list adapter bound to the cursor.
159 *         // SimpleListAdapter is designed for binding to a Cursor.
160 *         ListAdapter adapter = new SimpleCursorAdapter(
161 *                 this, // Context.
162 *                 android.R.layout.two_line_list_item,  // Specify the row template to use (here, two columns bound to the two retrieved cursor
163 * rows).
164 *                 mCursor,                                              // Pass in the cursor to bind to.
165 *                 new String[] {People.NAME, People.COMPANY},           // Array of cursor columns to bind to.
166 *                 new int[] {android.R.id.text1, android.R.id.text2});  // Parallel array of which template objects to bind to those columns.
167 *
168 *         // Bind to our new adapter.
169 *         setListAdapter(adapter);
170 *     }
171 * }
172 * </pre>
173 *
174 * @see #setListAdapter
175 * @see android.widget.ListView
176 */
177public class ListActivity extends Activity {
178    /**
179     * This field should be made private, so it is hidden from the SDK.
180     * {@hide}
181     */
182    protected ListAdapter mAdapter;
183    /**
184     * This field should be made private, so it is hidden from the SDK.
185     * {@hide}
186     */
187    protected ListView mList;
188
189    private Handler mHandler = new Handler();
190    private boolean mFinishedStart = false;
191
192    private Runnable mRequestFocus = new Runnable() {
193        public void run() {
194            mList.focusableViewAvailable(mList);
195        }
196    };
197
198    /**
199     * This method will be called when an item in the list is selected.
200     * Subclasses should override. Subclasses can call
201     * getListView().getItemAtPosition(position) if they need to access the
202     * data associated with the selected item.
203     *
204     * @param l The ListView where the click happened
205     * @param v The view that was clicked within the ListView
206     * @param position The position of the view in the list
207     * @param id The row id of the item that was clicked
208     */
209    protected void onListItemClick(ListView l, View v, int position, long id) {
210    }
211
212    /**
213     * Ensures the list view has been created before Activity restores all
214     * of the view states.
215     *
216     *@see Activity#onRestoreInstanceState(Bundle)
217     */
218    @Override
219    protected void onRestoreInstanceState(Bundle state) {
220        ensureList();
221        super.onRestoreInstanceState(state);
222    }
223
224    /**
225     * @see Activity#onDestroy()
226     */
227    @Override
228    protected void onDestroy() {
229        mHandler.removeCallbacks(mRequestFocus);
230        super.onDestroy();
231    }
232
233    /**
234     * Updates the screen state (current list and other views) when the
235     * content changes.
236     *
237     * @see Activity#onContentChanged()
238     */
239    @Override
240    public void onContentChanged() {
241        super.onContentChanged();
242        View emptyView = findViewById(com.android.internal.R.id.empty);
243        mList = (ListView)findViewById(com.android.internal.R.id.list);
244        if (mList == null) {
245            throw new RuntimeException(
246                    "Your content must have a ListView whose id attribute is " +
247                    "'android.R.id.list'");
248        }
249        if (emptyView != null) {
250            mList.setEmptyView(emptyView);
251        }
252        mList.setOnItemClickListener(mOnClickListener);
253        if (mFinishedStart) {
254            setListAdapter(mAdapter);
255        }
256        mHandler.post(mRequestFocus);
257        mFinishedStart = true;
258    }
259
260    /**
261     * Provide the cursor for the list view.
262     */
263    public void setListAdapter(ListAdapter adapter) {
264        synchronized (this) {
265            ensureList();
266            mAdapter = adapter;
267            mList.setAdapter(adapter);
268        }
269    }
270
271    /**
272     * Set the currently selected list item to the specified
273     * position with the adapter's data
274     *
275     * @param position
276     */
277    public void setSelection(int position) {
278        mList.setSelection(position);
279    }
280
281    /**
282     * Get the position of the currently selected list item.
283     */
284    public int getSelectedItemPosition() {
285        return mList.getSelectedItemPosition();
286    }
287
288    /**
289     * Get the cursor row ID of the currently selected list item.
290     */
291    public long getSelectedItemId() {
292        return mList.getSelectedItemId();
293    }
294
295    /**
296     * Get the activity's list view widget.
297     */
298    public ListView getListView() {
299        ensureList();
300        return mList;
301    }
302
303    /**
304     * Get the ListAdapter associated with this activity's ListView.
305     */
306    public ListAdapter getListAdapter() {
307        return mAdapter;
308    }
309
310    private void ensureList() {
311        if (mList != null) {
312            return;
313        }
314        setContentView(com.android.internal.R.layout.list_content);
315
316    }
317
318    private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
319        public void onItemClick(AdapterView parent, View v, int position, long id)
320        {
321            onListItemClick((ListView)parent, v, position, id);
322        }
323    };
324}
325