• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Cursor;
21 import android.net.Uri;
22 import android.view.View;
23 
24 /**
25  * An easy adapter to map columns from a cursor to TextViews or ImageViews
26  * defined in an XML file. You can specify which columns you want, which
27  * views you want to display the columns, and the XML file that defines
28  * the appearance of these views.
29  *
30  * Binding occurs in two phases. First, if a
31  * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
32  * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
33  * is invoked. If the returned value is true, binding has occured. If the
34  * returned value is false and the view to bind is a TextView,
35  * {@link #setViewText(TextView, String)} is invoked. If the returned value
36  * is false and the view to bind is an ImageView,
37  * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
38  * binding can be found, an {@link IllegalStateException} is thrown.
39  *
40  * If this adapter is used with filtering, for instance in an
41  * {@link android.widget.AutoCompleteTextView}, you can use the
42  * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the
43  * {@link android.widget.FilterQueryProvider} interfaces
44  * to get control over the filtering process. You can refer to
45  * {@link #convertToString(android.database.Cursor)} and
46  * {@link #runQueryOnBackgroundThread(CharSequence)} for more information.
47  */
48 public class SimpleCursorAdapter extends ResourceCursorAdapter {
49     /**
50      * A list of columns containing the data to bind to the UI.
51      * This field should be made private, so it is hidden from the SDK.
52      * {@hide}
53      */
54     protected int[] mFrom;
55     /**
56      * A list of View ids representing the views to which the data must be bound.
57      * This field should be made private, so it is hidden from the SDK.
58      * {@hide}
59      */
60     protected int[] mTo;
61 
62     private int mStringConversionColumn = -1;
63     private CursorToStringConverter mCursorToStringConverter;
64     private ViewBinder mViewBinder;
65 
66     String[] mOriginalFrom;
67 
68     /**
69      * Constructor the enables auto-requery.
70      *
71      * @deprecated This option is discouraged, as it results in Cursor queries
72      * being performed on the application's UI thread and thus can cause poor
73      * responsiveness or even Application Not Responding errors.  As an alternative,
74      * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
75      */
76     @Deprecated
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to)77     public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
78         super(context, layout, c);
79         mTo = to;
80         mOriginalFrom = from;
81         findColumns(c, from);
82     }
83 
84     /**
85      * Standard constructor.
86      *
87      * @param context The context where the ListView associated with this
88      *            SimpleListItemFactory is running
89      * @param layout resource identifier of a layout file that defines the views
90      *            for this list item. The layout file should include at least
91      *            those named views defined in "to"
92      * @param c The database cursor.  Can be null if the cursor is not available yet.
93      * @param from A list of column names representing the data to bind to the UI.  Can be null
94      *            if the cursor is not available yet.
95      * @param to The views that should display column in the "from" parameter.
96      *            These should all be TextViews. The first N views in this list
97      *            are given the values of the first N columns in the from
98      *            parameter.  Can be null if the cursor is not available yet.
99      * @param flags Flags used to determine the behavior of the adapter,
100      * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
101      */
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)102     public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
103             int[] to, int flags) {
104         super(context, layout, c, flags);
105         mTo = to;
106         mOriginalFrom = from;
107         findColumns(c, from);
108     }
109 
110     /**
111      * Binds all of the field names passed into the "to" parameter of the
112      * constructor with their corresponding cursor columns as specified in the
113      * "from" parameter.
114      *
115      * Binding occurs in two phases. First, if a
116      * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
117      * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
118      * is invoked. If the returned value is true, binding has occured. If the
119      * returned value is false and the view to bind is a TextView,
120      * {@link #setViewText(TextView, String)} is invoked. If the returned value is
121      * false and the view to bind is an ImageView,
122      * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
123      * binding can be found, an {@link IllegalStateException} is thrown.
124      *
125      * @throws IllegalStateException if binding cannot occur
126      *
127      * @see android.widget.CursorAdapter#bindView(android.view.View,
128      *      android.content.Context, android.database.Cursor)
129      * @see #getViewBinder()
130      * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
131      * @see #setViewImage(ImageView, String)
132      * @see #setViewText(TextView, String)
133      */
134     @Override
bindView(View view, Context context, Cursor cursor)135     public void bindView(View view, Context context, Cursor cursor) {
136         final ViewBinder binder = mViewBinder;
137         final int count = mTo.length;
138         final int[] from = mFrom;
139         final int[] to = mTo;
140 
141         for (int i = 0; i < count; i++) {
142             final View v = view.findViewById(to[i]);
143             if (v != null) {
144                 boolean bound = false;
145                 if (binder != null) {
146                     bound = binder.setViewValue(v, cursor, from[i]);
147                 }
148 
149                 if (!bound) {
150                     String text = cursor.getString(from[i]);
151                     if (text == null) {
152                         text = "";
153                     }
154 
155                     if (v instanceof TextView) {
156                         setViewText((TextView) v, text);
157                     } else if (v instanceof ImageView) {
158                         setViewImage((ImageView) v, text);
159                     } else {
160                         throw new IllegalStateException(v.getClass().getName() + " is not a " +
161                                 " view that can be bounds by this SimpleCursorAdapter");
162                     }
163                 }
164             }
165         }
166     }
167 
168     /**
169      * Returns the {@link ViewBinder} used to bind data to views.
170      *
171      * @return a ViewBinder or null if the binder does not exist
172      *
173      * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
174      * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
175      */
getViewBinder()176     public ViewBinder getViewBinder() {
177         return mViewBinder;
178     }
179 
180     /**
181      * Sets the binder used to bind data to views.
182      *
183      * @param viewBinder the binder used to bind data to views, can be null to
184      *        remove the existing binder
185      *
186      * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
187      * @see #getViewBinder()
188      */
setViewBinder(ViewBinder viewBinder)189     public void setViewBinder(ViewBinder viewBinder) {
190         mViewBinder = viewBinder;
191     }
192 
193     /**
194      * Called by bindView() to set the image for an ImageView but only if
195      * there is no existing ViewBinder or if the existing ViewBinder cannot
196      * handle binding to an ImageView.
197      *
198      * By default, the value will be treated as an image resource. If the
199      * value cannot be used as an image resource, the value is used as an
200      * image Uri.
201      *
202      * Intended to be overridden by Adapters that need to filter strings
203      * retrieved from the database.
204      *
205      * @param v ImageView to receive an image
206      * @param value the value retrieved from the cursor
207      */
setViewImage(ImageView v, String value)208     public void setViewImage(ImageView v, String value) {
209         try {
210             v.setImageResource(Integer.parseInt(value));
211         } catch (NumberFormatException nfe) {
212             v.setImageURI(Uri.parse(value));
213         }
214     }
215 
216     /**
217      * Called by bindView() to set the text for a TextView but only if
218      * there is no existing ViewBinder or if the existing ViewBinder cannot
219      * handle binding to a TextView.
220      *
221      * Intended to be overridden by Adapters that need to filter strings
222      * retrieved from the database.
223      *
224      * @param v TextView to receive text
225      * @param text the text to be set for the TextView
226      */
setViewText(TextView v, String text)227     public void setViewText(TextView v, String text) {
228         v.setText(text);
229     }
230 
231     /**
232      * Return the index of the column used to get a String representation
233      * of the Cursor.
234      *
235      * @return a valid index in the current Cursor or -1
236      *
237      * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
238      * @see #setStringConversionColumn(int)
239      * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
240      * @see #getCursorToStringConverter()
241      */
getStringConversionColumn()242     public int getStringConversionColumn() {
243         return mStringConversionColumn;
244     }
245 
246     /**
247      * Defines the index of the column in the Cursor used to get a String
248      * representation of that Cursor. The column is used to convert the
249      * Cursor to a String only when the current CursorToStringConverter
250      * is null.
251      *
252      * @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
253      *        conversion mechanism
254      *
255      * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
256      * @see #getStringConversionColumn()
257      * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
258      * @see #getCursorToStringConverter()
259      */
setStringConversionColumn(int stringConversionColumn)260     public void setStringConversionColumn(int stringConversionColumn) {
261         mStringConversionColumn = stringConversionColumn;
262     }
263 
264     /**
265      * Returns the converter used to convert the filtering Cursor
266      * into a String.
267      *
268      * @return null if the converter does not exist or an instance of
269      *         {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
270      *
271      * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
272      * @see #getStringConversionColumn()
273      * @see #setStringConversionColumn(int)
274      * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
275      */
getCursorToStringConverter()276     public CursorToStringConverter getCursorToStringConverter() {
277         return mCursorToStringConverter;
278     }
279 
280     /**
281      * Sets the converter  used to convert the filtering Cursor
282      * into a String.
283      *
284      * @param cursorToStringConverter the Cursor to String converter, or
285      *        null to remove the converter
286      *
287      * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
288      * @see #getStringConversionColumn()
289      * @see #setStringConversionColumn(int)
290      * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
291      */
setCursorToStringConverter(CursorToStringConverter cursorToStringConverter)292     public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
293         mCursorToStringConverter = cursorToStringConverter;
294     }
295 
296     /**
297      * Returns a CharSequence representation of the specified Cursor as defined
298      * by the current CursorToStringConverter. If no CursorToStringConverter
299      * has been set, the String conversion column is used instead. If the
300      * conversion column is -1, the returned String is empty if the cursor
301      * is null or Cursor.toString().
302      *
303      * @param cursor the Cursor to convert to a CharSequence
304      *
305      * @return a non-null CharSequence representing the cursor
306      */
307     @Override
convertToString(Cursor cursor)308     public CharSequence convertToString(Cursor cursor) {
309         if (mCursorToStringConverter != null) {
310             return mCursorToStringConverter.convertToString(cursor);
311         } else if (mStringConversionColumn > -1) {
312             return cursor.getString(mStringConversionColumn);
313         }
314 
315         return super.convertToString(cursor);
316     }
317 
318     /**
319      * Create a map from an array of strings to an array of column-id integers in cursor c.
320      * If c is null, the array will be discarded.
321      *
322      * @param c the cursor to find the columns from
323      * @param from the Strings naming the columns of interest
324      */
findColumns(Cursor c, String[] from)325     private void findColumns(Cursor c, String[] from) {
326         if (c != null) {
327             int i;
328             int count = from.length;
329             if (mFrom == null || mFrom.length != count) {
330                 mFrom = new int[count];
331             }
332             for (i = 0; i < count; i++) {
333                 mFrom[i] = c.getColumnIndexOrThrow(from[i]);
334             }
335         } else {
336             mFrom = null;
337         }
338     }
339 
340     @Override
swapCursor(Cursor c)341     public Cursor swapCursor(Cursor c) {
342         // super.swapCursor() will notify observers before we have
343         // a valid mapping, make sure we have a mapping before this
344         // happens
345         findColumns(c, mOriginalFrom);
346         return super.swapCursor(c);
347     }
348 
349     /**
350      * Change the cursor and change the column-to-view mappings at the same time.
351      *
352      * @param c The database cursor.  Can be null if the cursor is not available yet.
353      * @param from A list of column names representing the data to bind to the UI.  Can be null
354      *            if the cursor is not available yet.
355      * @param to The views that should display column in the "from" parameter.
356      *            These should all be TextViews. The first N views in this list
357      *            are given the values of the first N columns in the from
358      *            parameter.  Can be null if the cursor is not available yet.
359      */
changeCursorAndColumns(Cursor c, String[] from, int[] to)360     public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
361         mOriginalFrom = from;
362         mTo = to;
363         // super.changeCursor() will notify observers before we have
364         // a valid mapping, make sure we have a mapping before this
365         // happens
366         findColumns(c, mOriginalFrom);
367         super.changeCursor(c);
368     }
369 
370     /**
371      * This class can be used by external clients of SimpleCursorAdapter
372      * to bind values fom the Cursor to views.
373      *
374      * You should use this class to bind values from the Cursor to views
375      * that are not directly supported by SimpleCursorAdapter or to
376      * change the way binding occurs for views supported by
377      * SimpleCursorAdapter.
378      *
379      * @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
380      * @see SimpleCursorAdapter#setViewImage(ImageView, String)
381      * @see SimpleCursorAdapter#setViewText(TextView, String)
382      */
383     public static interface ViewBinder {
384         /**
385          * Binds the Cursor column defined by the specified index to the specified view.
386          *
387          * When binding is handled by this ViewBinder, this method must return true.
388          * If this method returns false, SimpleCursorAdapter will attempts to handle
389          * the binding on its own.
390          *
391          * @param view the view to bind the data to
392          * @param cursor the cursor to get the data from
393          * @param columnIndex the column at which the data can be found in the cursor
394          *
395          * @return true if the data was bound to the view, false otherwise
396          */
setViewValue(View view, Cursor cursor, int columnIndex)397         boolean setViewValue(View view, Cursor cursor, int columnIndex);
398     }
399 
400     /**
401      * This class can be used by external clients of SimpleCursorAdapter
402      * to define how the Cursor should be converted to a String.
403      *
404      * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
405      */
406     public static interface CursorToStringConverter {
407         /**
408          * Returns a CharSequence representing the specified Cursor.
409          *
410          * @param cursor the cursor for which a CharSequence representation
411          *        is requested
412          *
413          * @return a non-null CharSequence representing the cursor
414          */
convertToString(Cursor cursor)415         CharSequence convertToString(Cursor cursor);
416     }
417 
418 }
419