OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / widget / CursorAdapter.java
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
17 package android.widget;
18
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.database.Cursor;
22 import android.database.DataSetObserver;
23 import android.os.Handler;
24 import android.util.Config;
25 import android.util.Log;
26 import android.view.View;
27 import android.view.ViewGroup;
28
29 /**
30  * Adapter that exposes data from a {@link android.database.Cursor Cursor} to a 
31  * {@link android.widget.ListView ListView} widget. The Cursor must include 
32  * a column named "_id" or this class will not work.
33  */
34 public abstract class CursorAdapter extends BaseAdapter implements Filterable,
35         CursorFilter.CursorFilterClient {
36     /**
37      * This field should be made private, so it is hidden from the SDK.
38      * {@hide}
39      */
40     protected boolean mDataValid;
41     /**
42      * This field should be made private, so it is hidden from the SDK.
43      * {@hide}
44      */
45     protected boolean mAutoRequery;
46     /**
47      * This field should be made private, so it is hidden from the SDK.
48      * {@hide}
49      */
50     protected Cursor mCursor;
51     /**
52      * This field should be made private, so it is hidden from the SDK.
53      * {@hide}
54      */
55     protected Context mContext;
56     /**
57      * This field should be made private, so it is hidden from the SDK.
58      * {@hide}
59      */
60     protected int mRowIDColumn;
61     /**
62      * This field should be made private, so it is hidden from the SDK.
63      * {@hide}
64      */
65     protected ChangeObserver mChangeObserver;
66     /**
67      * This field should be made private, so it is hidden from the SDK.
68      * {@hide}
69      */
70     protected DataSetObserver mDataSetObserver = new MyDataSetObserver();
71     /**
72      * This field should be made private, so it is hidden from the SDK.
73      * {@hide}
74      */
75     protected CursorFilter mCursorFilter;
76     /**
77      * This field should be made private, so it is hidden from the SDK.
78      * {@hide}
79      */
80     protected FilterQueryProvider mFilterQueryProvider;
81
82     /**
83      * Constructor. The adapter will call requery() on the cursor whenever
84      * it changes so that the most recent data is always displayed.
85      *
86      * @param c The cursor from which to get the data.
87      * @param context The context
88      */
89     public CursorAdapter(Context context, Cursor c) {
90         init(context, c, true);
91     }
92
93     /**
94      * Constructor
95      * @param c The cursor from which to get the data.
96      * @param context The context
97      * @param autoRequery If true the adapter will call requery() on the
98      *                    cursor whenever it changes so the most recent
99      *                    data is always displayed.
100      */
101     public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
102         init(context, c, autoRequery);
103     }
104
105     protected void init(Context context, Cursor c, boolean autoRequery) {
106         boolean cursorPresent = c != null;
107         mAutoRequery = autoRequery;
108         mCursor = c;
109         mDataValid = cursorPresent;
110         mContext = context;
111         mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
112         mChangeObserver = new ChangeObserver();
113         if (cursorPresent) {
114             c.registerContentObserver(mChangeObserver);
115             c.registerDataSetObserver(mDataSetObserver);
116         }
117     }
118
119     /**
120      * Returns the cursor.
121      * @return the cursor.
122      */
123     public Cursor getCursor() {
124         return mCursor;
125     }
126
127     /**
128      * @see android.widget.ListAdapter#getCount()
129      */
130     public int getCount() {
131         if (mDataValid && mCursor != null) {
132             return mCursor.getCount();
133         } else {
134             return 0;
135         }
136     }
137     
138     /**
139      * @see android.widget.ListAdapter#getItem(int)
140      */
141     public Object getItem(int position) {
142         if (mDataValid && mCursor != null) {
143             mCursor.moveToPosition(position);
144             return mCursor;
145         } else {
146             return null;
147         }
148     }
149
150     /**
151      * @see android.widget.ListAdapter#getItemId(int)
152      */
153     public long getItemId(int position) {
154         if (mDataValid && mCursor != null) {
155             if (mCursor.moveToPosition(position)) {
156                 return mCursor.getLong(mRowIDColumn);
157             } else {
158                 return 0;
159             }
160         } else {
161             return 0;
162         }
163     }
164     
165     @Override
166     public boolean hasStableIds() {
167         return true;
168     }
169
170     /**
171      * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
172      */
173     public View getView(int position, View convertView, ViewGroup parent) {
174         if (!mDataValid) {
175             throw new IllegalStateException("this should only be called when the cursor is valid");
176         }
177         if (!mCursor.moveToPosition(position)) {
178             throw new IllegalStateException("couldn't move cursor to position " + position);
179         }
180         View v;
181         if (convertView == null) {
182             v = newView(mContext, mCursor, parent);
183         } else {
184             v = convertView;
185         }
186         bindView(v, mContext, mCursor);
187         return v;
188     }
189
190     @Override
191     public View getDropDownView(int position, View convertView, ViewGroup parent) {
192         if (mDataValid) {
193             mCursor.moveToPosition(position);
194             View v;
195             if (convertView == null) {
196                 v = newDropDownView(mContext, mCursor, parent);
197             } else {
198                 v = convertView;
199             }
200             bindView(v, mContext, mCursor);
201             return v;
202         } else {
203             return null;
204         }
205     }
206     
207     /**
208      * Makes a new view to hold the data pointed to by cursor.
209      * @param context Interface to application's global information
210      * @param cursor The cursor from which to get the data. The cursor is already
211      * moved to the correct position.
212      * @param parent The parent to which the new view is attached to
213      * @return the newly created view.
214      */
215     public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
216
217     /**
218      * Makes a new drop down view to hold the data pointed to by cursor.
219      * @param context Interface to application's global information
220      * @param cursor The cursor from which to get the data. The cursor is already
221      * moved to the correct position.
222      * @param parent The parent to which the new view is attached to
223      * @return the newly created view.
224      */
225     public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
226         return newView(context, cursor, parent);
227     }
228
229     /**
230      * Bind an existing view to the data pointed to by cursor
231      * @param view Existing view, returned earlier by newView
232      * @param context Interface to application's global information
233      * @param cursor The cursor from which to get the data. The cursor is already
234      * moved to the correct position.
235      */
236     public abstract void bindView(View view, Context context, Cursor cursor);
237     
238     /**
239      * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
240      * closed.
241      * 
242      * @param cursor the new cursor to be used
243      */
244     public void changeCursor(Cursor cursor) {
245         if (cursor == mCursor) {
246             return;
247         }
248         if (mCursor != null) {
249             mCursor.unregisterContentObserver(mChangeObserver);
250             mCursor.unregisterDataSetObserver(mDataSetObserver);
251             mCursor.close();
252         }
253         mCursor = cursor;
254         if (cursor != null) {
255             cursor.registerContentObserver(mChangeObserver);
256             cursor.registerDataSetObserver(mDataSetObserver);
257             mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
258             mDataValid = true;
259             // notify the observers about the new cursor
260             notifyDataSetChanged();
261         } else {
262             mRowIDColumn = -1;
263             mDataValid = false;
264             // notify the observers about the lack of a data set
265             notifyDataSetInvalidated();
266         }
267     }
268
269     /**
270      * <p>Converts the cursor into a CharSequence. Subclasses should override this
271      * method to convert their results. The default implementation returns an
272      * empty String for null values or the default String representation of
273      * the value.</p>
274      *
275      * @param cursor the cursor to convert to a CharSequence
276      * @return a CharSequence representing the value
277      */
278     public CharSequence convertToString(Cursor cursor) {
279         return cursor == null ? "" : cursor.toString();
280     }
281
282     /**
283      * Runs a query with the specified constraint. This query is requested
284      * by the filter attached to this adapter.
285      *
286      * The query is provided by a
287      * {@link android.widget.FilterQueryProvider}.
288      * If no provider is specified, the current cursor is not filtered and returned.
289      *
290      * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
291      * and the previous cursor is closed.
292      *
293      * This method is always executed on a background thread, not on the
294      * application's main thread (or UI thread.)
295      * 
296      * Contract: when constraint is null or empty, the original results,
297      * prior to any filtering, must be returned.
298      *
299      * @param constraint the constraint with which the query must be filtered
300      *
301      * @return a Cursor representing the results of the new query
302      *
303      * @see #getFilter()
304      * @see #getFilterQueryProvider()
305      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
306      */
307     public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
308         if (mFilterQueryProvider != null) {
309             return mFilterQueryProvider.runQuery(constraint);
310         }
311
312         return mCursor;
313     }
314
315     public Filter getFilter() {
316         if (mCursorFilter == null) {
317             mCursorFilter = new CursorFilter(this);
318         }
319         return mCursorFilter;
320     }
321
322     /**
323      * Returns the query filter provider used for filtering. When the
324      * provider is null, no filtering occurs.
325      *
326      * @return the current filter query provider or null if it does not exist
327      *
328      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
329      * @see #runQueryOnBackgroundThread(CharSequence)
330      */
331     public FilterQueryProvider getFilterQueryProvider() {
332         return mFilterQueryProvider;
333     }
334
335     /**
336      * Sets the query filter provider used to filter the current Cursor.
337      * The provider's
338      * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
339      * method is invoked when filtering is requested by a client of
340      * this adapter.
341      *
342      * @param filterQueryProvider the filter query provider or null to remove it
343      *
344      * @see #getFilterQueryProvider()
345      * @see #runQueryOnBackgroundThread(CharSequence)
346      */
347     public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
348         mFilterQueryProvider = filterQueryProvider;
349     }
350
351     /**
352      * Called when the {@link ContentObserver} on the cursor receives a change notification.
353      * The default implementation provides the auto-requery logic, but may be overridden by
354      * sub classes.
355      * 
356      * @see ContentObserver#onChange(boolean)
357      */
358     protected void onContentChanged() {
359         if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
360             if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
361             mDataValid = mCursor.requery();
362         }
363     }
364
365     private class ChangeObserver extends ContentObserver {
366         public ChangeObserver() {
367             super(new Handler());
368         }
369
370         @Override
371         public boolean deliverSelfNotifications() {
372             return true;
373         }
374
375         @Override
376         public void onChange(boolean selfChange) {
377             onContentChanged();
378         }
379     }
380
381     private class MyDataSetObserver extends DataSetObserver {
382         @Override
383         public void onChanged() {
384             mDataValid = true;
385             notifyDataSetChanged();
386         }
387
388         @Override
389         public void onInvalidated() {
390             mDataValid = false;
391             notifyDataSetInvalidated();
392         }
393     }
394
395 }