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 views
27  * you want to display the columns, and the XML file that defines the appearance
28  * of these views. Separate XML files for child and groups are possible.
29  *
30  * Binding occurs in two phases. First, if a
31  * {@link android.widget.SimpleCursorTreeAdapter.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 occurred. 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 public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter {
41 
42     /** The name of the columns that contain the data to display for a group. */
43     private String[] mGroupFromNames;
44 
45     /** The indices of columns that contain data to display for a group. */
46     private int[] mGroupFrom;
47     /**
48      * The View IDs that will display a group's data fetched from the
49      * corresponding column.
50      */
51     private int[] mGroupTo;
52 
53     /** The name of the columns that contain the data to display for a child. */
54     private String[] mChildFromNames;
55 
56     /** The indices of columns that contain data to display for a child. */
57     private int[] mChildFrom;
58     /**
59      * The View IDs that will display a child's data fetched from the
60      * corresponding column.
61      */
62     private int[] mChildTo;
63 
64     /**
65      * View binder, if supplied
66      */
67     private ViewBinder mViewBinder;
68 
69     /**
70      * Constructor.
71      *
72      * @param context The context where the {@link ExpandableListView}
73      *            associated with this {@link SimpleCursorTreeAdapter} is
74      *            running
75      * @param cursor The database cursor
76      * @param collapsedGroupLayout The resource identifier of a layout file that
77      *            defines the views for a collapsed group. The layout file
78      *            should include at least those named views defined in groupTo.
79      * @param expandedGroupLayout The resource identifier of a layout file that
80      *            defines the views for an expanded group. The layout file
81      *            should include at least those named views defined in groupTo.
82      * @param groupFrom A list of column names that will be used to display the
83      *            data for a group.
84      * @param groupTo The group views (from the group layouts) that should
85      *            display column in the "from" parameter. These should all be
86      *            TextViews or ImageViews. The first N views in this list are
87      *            given the values of the first N columns in the from parameter.
88      * @param childLayout The resource identifier of a layout file that defines
89      *            the views for a child (except the last). The layout file
90      *            should include at least those named views defined in childTo.
91      * @param lastChildLayout The resource identifier of a layout file that
92      *            defines the views for the last child within a group. The
93      *            layout file should include at least those named views defined
94      *            in childTo.
95      * @param childFrom A list of column names that will be used to display the
96      *            data for a child.
97      * @param childTo The child views (from the child layouts) that should
98      *            display column in the "from" parameter. These should all be
99      *            TextViews or ImageViews. The first N views in this list are
100      *            given the values of the first N columns in the from parameter.
101      */
SimpleCursorTreeAdapter(Context context, Cursor cursor, int collapsedGroupLayout, int expandedGroupLayout, String[] groupFrom, int[] groupTo, int childLayout, int lastChildLayout, String[] childFrom, int[] childTo)102     public SimpleCursorTreeAdapter(Context context, Cursor cursor, int collapsedGroupLayout,
103             int expandedGroupLayout, String[] groupFrom, int[] groupTo, int childLayout,
104             int lastChildLayout, String[] childFrom, int[] childTo) {
105         super(context, cursor, collapsedGroupLayout, expandedGroupLayout, childLayout,
106                 lastChildLayout);
107         init(groupFrom, groupTo, childFrom, childTo);
108     }
109 
110     /**
111      * Constructor.
112      *
113      * @param context The context where the {@link ExpandableListView}
114      *            associated with this {@link SimpleCursorTreeAdapter} is
115      *            running
116      * @param cursor The database cursor
117      * @param collapsedGroupLayout The resource identifier of a layout file that
118      *            defines the views for a collapsed group. The layout file
119      *            should include at least those named views defined in groupTo.
120      * @param expandedGroupLayout The resource identifier of a layout file that
121      *            defines the views for an expanded group. The layout file
122      *            should include at least those named views defined in groupTo.
123      * @param groupFrom A list of column names that will be used to display the
124      *            data for a group.
125      * @param groupTo The group views (from the group layouts) that should
126      *            display column in the "from" parameter. These should all be
127      *            TextViews or ImageViews. The first N views in this list are
128      *            given the values of the first N columns in the from parameter.
129      * @param childLayout The resource identifier of a layout file that defines
130      *            the views for a child. The layout file
131      *            should include at least those named views defined in childTo.
132      * @param childFrom A list of column names that will be used to display the
133      *            data for a child.
134      * @param childTo The child views (from the child layouts) that should
135      *            display column in the "from" parameter. These should all be
136      *            TextViews or ImageViews. The first N views in this list are
137      *            given the values of the first N columns in the from parameter.
138      */
SimpleCursorTreeAdapter(Context context, Cursor cursor, int collapsedGroupLayout, int expandedGroupLayout, String[] groupFrom, int[] groupTo, int childLayout, String[] childFrom, int[] childTo)139     public SimpleCursorTreeAdapter(Context context, Cursor cursor, int collapsedGroupLayout,
140             int expandedGroupLayout, String[] groupFrom, int[] groupTo,
141             int childLayout, String[] childFrom, int[] childTo) {
142         super(context, cursor, collapsedGroupLayout, expandedGroupLayout, childLayout);
143         init(groupFrom, groupTo, childFrom, childTo);
144     }
145 
146     /**
147      * Constructor.
148      *
149      * @param context The context where the {@link ExpandableListView}
150      *            associated with this {@link SimpleCursorTreeAdapter} is
151      *            running
152      * @param cursor The database cursor
153      * @param groupLayout The resource identifier of a layout file that defines
154      *            the views for a group. The layout file should include at least
155      *            those named views defined in groupTo.
156      * @param groupFrom A list of column names that will be used to display the
157      *            data for a group.
158      * @param groupTo The group views (from the group layouts) that should
159      *            display column in the "from" parameter. These should all be
160      *            TextViews or ImageViews. The first N views in this list are
161      *            given the values of the first N columns in the from parameter.
162      * @param childLayout The resource identifier of a layout file that defines
163      *            the views for a child. The layout file should include at least
164      *            those named views defined in childTo.
165      * @param childFrom A list of column names that will be used to display the
166      *            data for a child.
167      * @param childTo The child views (from the child layouts) that should
168      *            display column in the "from" parameter. These should all be
169      *            TextViews or ImageViews. The first N views in this list are
170      *            given the values of the first N columns in the from parameter.
171      */
SimpleCursorTreeAdapter(Context context, Cursor cursor, int groupLayout, String[] groupFrom, int[] groupTo, int childLayout, String[] childFrom, int[] childTo)172     public SimpleCursorTreeAdapter(Context context, Cursor cursor, int groupLayout,
173             String[] groupFrom, int[] groupTo, int childLayout, String[] childFrom,
174             int[] childTo) {
175         super(context, cursor, groupLayout, childLayout);
176         init(groupFrom, groupTo, childFrom, childTo);
177     }
178 
init(String[] groupFromNames, int[] groupTo, String[] childFromNames, int[] childTo)179     private void init(String[] groupFromNames, int[] groupTo, String[] childFromNames,
180             int[] childTo) {
181 
182         mGroupFromNames = groupFromNames;
183         mGroupTo = groupTo;
184 
185         mChildFromNames = childFromNames;
186         mChildTo = childTo;
187     }
188 
189     /**
190      * Returns the {@link ViewBinder} used to bind data to views.
191      *
192      * @return a ViewBinder or null if the binder does not exist
193      *
194      * @see #setViewBinder(android.widget.SimpleCursorTreeAdapter.ViewBinder)
195      */
getViewBinder()196     public ViewBinder getViewBinder() {
197         return mViewBinder;
198     }
199 
200     /**
201      * Sets the binder used to bind data to views.
202      *
203      * @param viewBinder the binder used to bind data to views, can be null to
204      *        remove the existing binder
205      *
206      * @see #getViewBinder()
207      */
setViewBinder(ViewBinder viewBinder)208     public void setViewBinder(ViewBinder viewBinder) {
209         mViewBinder = viewBinder;
210     }
211 
bindView(View view, Context context, Cursor cursor, int[] from, int[] to)212     private void bindView(View view, Context context, Cursor cursor, int[] from, int[] to) {
213         final ViewBinder binder = mViewBinder;
214 
215         for (int i = 0; i < to.length; i++) {
216             View v = view.findViewById(to[i]);
217             if (v != null) {
218                 boolean bound = false;
219                 if (binder != null) {
220                     bound = binder.setViewValue(v, cursor, from[i]);
221                 }
222 
223                 if (!bound) {
224                     String text = cursor.getString(from[i]);
225                     if (text == null) {
226                         text = "";
227                     }
228                     if (v instanceof TextView) {
229                         setViewText((TextView) v, text);
230                     } else if (v instanceof ImageView) {
231                         setViewImage((ImageView) v, text);
232                     } else {
233                         throw new IllegalStateException("SimpleCursorTreeAdapter can bind values" +
234                                 " only to TextView and ImageView!");
235                     }
236                 }
237             }
238         }
239     }
240 
initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns)241     private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) {
242         for (int i = fromColumnNames.length - 1; i >= 0; i--) {
243             fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]);
244         }
245     }
246 
247     @Override
bindChildView(View view, Context context, Cursor cursor, boolean isLastChild)248     protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
249         if (mChildFrom == null) {
250             mChildFrom = new int[mChildFromNames.length];
251             initFromColumns(cursor, mChildFromNames, mChildFrom);
252         }
253 
254         bindView(view, context, cursor, mChildFrom, mChildTo);
255     }
256 
257     @Override
bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded)258     protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
259         if (mGroupFrom == null) {
260             mGroupFrom = new int[mGroupFromNames.length];
261             initFromColumns(cursor, mGroupFromNames, mGroupFrom);
262         }
263 
264         bindView(view, context, cursor, mGroupFrom, mGroupTo);
265     }
266 
267     /**
268      * Called by bindView() to set the image for an ImageView. By default, the
269      * value will be treated as a Uri. Intended to be overridden by Adapters
270      * that need to filter strings retrieved from the database.
271      *
272      * @param v ImageView to receive an image
273      * @param value the value retrieved from the cursor
274      */
setViewImage(ImageView v, String value)275     protected void setViewImage(ImageView v, String value) {
276         try {
277             v.setImageResource(Integer.parseInt(value));
278         } catch (NumberFormatException nfe) {
279             v.setImageURI(Uri.parse(value));
280         }
281     }
282 
283     /**
284      * Called by bindView() to set the text for a TextView but only if
285      * there is no existing ViewBinder or if the existing ViewBinder cannot
286      * handle binding to a TextView.
287      *
288      * Intended to be overridden by Adapters that need to filter strings
289      * retrieved from the database.
290      *
291      * @param v TextView to receive text
292      * @param text the text to be set for the TextView
293      */
setViewText(TextView v, String text)294     public void setViewText(TextView v, String text) {
295         v.setText(text);
296     }
297 
298     /**
299      * This class can be used by external clients of SimpleCursorTreeAdapter
300      * to bind values from the Cursor to views.
301      *
302      * You should use this class to bind values from the Cursor to views
303      * that are not directly supported by SimpleCursorTreeAdapter or to
304      * change the way binding occurs for views supported by
305      * SimpleCursorTreeAdapter.
306      *
307      * @see SimpleCursorTreeAdapter#setViewImage(ImageView, String)
308      * @see SimpleCursorTreeAdapter#setViewText(TextView, String)
309      */
310     public static interface ViewBinder {
311         /**
312          * Binds the Cursor column defined by the specified index to the specified view.
313          *
314          * When binding is handled by this ViewBinder, this method must return true.
315          * If this method returns false, SimpleCursorTreeAdapter will attempts to handle
316          * the binding on its own.
317          *
318          * @param view the view to bind the data to
319          * @param cursor the cursor to get the data from
320          * @param columnIndex the column at which the data can be found in the cursor
321          *
322          * @return true if the data was bound to the view, false otherwise
323          */
setViewValue(View view, Cursor cursor, int columnIndex)324         boolean setViewValue(View view, Cursor cursor, int columnIndex);
325     }
326 }
327