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