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