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
17package android.widget;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.net.Uri;
22import 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 */
40public 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     */
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     */
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     */
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
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     */
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     */
208    public void setViewBinder(ViewBinder viewBinder) {
209        mViewBinder = viewBinder;
210    }
211
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
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
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
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     */
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     */
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         */
324        boolean setViewValue(View view, Cursor cursor, int columnIndex);
325    }
326}
327