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