OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / widget / ArrayAdapter.java
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.util.Log;
21 import android.view.LayoutInflater;
22 import android.view.View;
23 import android.view.ViewGroup;
24
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Comparator;
29 import java.util.Collections;
30
31 /**
32  * A ListAdapter that manages a ListView backed by an array of arbitrary
33  * objects.  By default this class expects that the provided resource id references
34  * a single TextView.  If you want to use a more complex layout, use the constructors that
35  * also takes a field id.  That field id should reference a TextView in the larger layout
36  * resource.
37  *
38  * However the TextView is referenced, it will be filled with the toString() of each object in
39  * the array. You can add lists or arrays of custom objects. Override the toString() method
40  * of your objects to determine what text will be displayed for the item in the list.
41  *
42  * To use something other than TextViews for the array display, for instance, ImageViews,
43  * or to have some of data besides toString() results fill the views,
44  * override {@link #getView(int, View, ViewGroup)} to return the type of view you want.
45  */
46 public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
47     /**
48      * Contains the list of objects that represent the data of this ArrayAdapter.
49      * The content of this list is referred to as "the array" in the documentation.
50      */
51     private List<T> mObjects;
52
53     /**
54      * Lock used to modify the content of {@link #mObjects}. Any write operation
55      * performed on the array should be synchronized on this lock. This lock is also
56      * used by the filter (see {@link #getFilter()} to make a synchronized copy of
57      * the original array of data.
58      */
59     private final Object mLock = new Object();
60
61     /**
62      * The resource indicating what views to inflate to display the content of this
63      * array adapter.
64      */
65     private int mResource;
66
67     /**
68      * The resource indicating what views to inflate to display the content of this
69      * array adapter in a drop down widget.
70      */
71     private int mDropDownResource;
72
73     /**
74      * If the inflated resource is not a TextView, {@link #mFieldId} is used to find
75      * a TextView inside the inflated views hierarchy. This field must contain the
76      * identifier that matches the one defined in the resource file.
77      */
78     private int mFieldId = 0;
79
80     /**
81      * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
82      * {@link #mObjects} is modified.
83      */
84     private boolean mNotifyOnChange = true;
85
86     private Context mContext;    
87
88     private ArrayList<T> mOriginalValues;
89     private ArrayFilter mFilter;
90
91     private LayoutInflater mInflater;
92
93     /**
94      * Constructor
95      *
96      * @param context The current context.
97      * @param textViewResourceId The resource ID for a layout file containing a TextView to use when
98      *                 instantiating views.
99      */
100     public ArrayAdapter(Context context, int textViewResourceId) {
101         init(context, textViewResourceId, 0, new ArrayList<T>());
102     }
103
104     /**
105      * Constructor
106      *
107      * @param context The current context.
108      * @param resource The resource ID for a layout file containing a layout to use when
109      *                 instantiating views.
110      * @param textViewResourceId The id of the TextView within the layout resource to be populated
111      */
112     public ArrayAdapter(Context context, int resource, int textViewResourceId) {
113         init(context, resource, textViewResourceId, new ArrayList<T>());
114     }
115
116     /**
117      * Constructor
118      *
119      * @param context The current context.
120      * @param textViewResourceId The resource ID for a layout file containing a TextView to use when
121      *                 instantiating views.
122      * @param objects The objects to represent in the ListView.
123      */
124     public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
125         init(context, textViewResourceId, 0, Arrays.asList(objects));
126     }
127
128     /**
129      * Constructor
130      *
131      * @param context The current context.
132      * @param resource The resource ID for a layout file containing a layout to use when
133      *                 instantiating views.
134      * @param textViewResourceId The id of the TextView within the layout resource to be populated
135      * @param objects The objects to represent in the ListView.
136      */
137     public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) {
138         init(context, resource, textViewResourceId, Arrays.asList(objects));
139     }
140
141     /**
142      * Constructor
143      *
144      * @param context The current context.
145      * @param textViewResourceId The resource ID for a layout file containing a TextView to use when
146      *                 instantiating views.
147      * @param objects The objects to represent in the ListView.
148      */
149     public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) {
150         init(context, textViewResourceId, 0, objects);
151     }
152
153     /**
154      * Constructor
155      *
156      * @param context The current context.
157      * @param resource The resource ID for a layout file containing a layout to use when
158      *                 instantiating views.
159      * @param textViewResourceId The id of the TextView within the layout resource to be populated
160      * @param objects The objects to represent in the ListView.
161      */
162     public ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects) {
163         init(context, resource, textViewResourceId, objects);
164     }
165
166     /**
167      * Adds the specified object at the end of the array.
168      *
169      * @param object The object to add at the end of the array.
170      */
171     public void add(T object) {
172         if (mOriginalValues != null) {
173             synchronized (mLock) {
174                 mOriginalValues.add(object);
175                 if (mNotifyOnChange) notifyDataSetChanged();
176             }
177         } else {
178             mObjects.add(object);
179             if (mNotifyOnChange) notifyDataSetChanged();
180         }
181     }
182
183     /**
184      * Inserts the specified object at the specified index in the array.
185      *
186      * @param object The object to insert into the array.
187      * @param index The index at which the object must be inserted.
188      */
189     public void insert(T object, int index) {
190         if (mOriginalValues != null) {
191             synchronized (mLock) {
192                 mOriginalValues.add(index, object);
193                 if (mNotifyOnChange) notifyDataSetChanged();
194             }
195         } else {
196             mObjects.add(index, object);
197             if (mNotifyOnChange) notifyDataSetChanged();
198         }
199     }
200
201     /**
202      * Removes the specified object from the array.
203      *
204      * @param object The object to remove.
205      */
206     public void remove(T object) {
207         if (mOriginalValues != null) {
208             synchronized (mLock) {
209                 mOriginalValues.remove(object);
210             }
211         } else {
212             mObjects.remove(object);
213         }
214         if (mNotifyOnChange) notifyDataSetChanged();
215     }
216
217     /**
218      * Remove all elements from the list.
219      */
220     public void clear() {
221         if (mOriginalValues != null) {
222             synchronized (mLock) {
223                 mOriginalValues.clear();
224             }
225         } else {
226             mObjects.clear();
227         }
228         if (mNotifyOnChange) notifyDataSetChanged();
229     }
230
231     /**
232      * Sorts the content of this adapter using the specified comparator.
233      *
234      * @param comparator The comparator used to sort the objects contained
235      *        in this adapter.
236      */
237     public void sort(Comparator<? super T> comparator) {
238         Collections.sort(mObjects, comparator);
239         if (mNotifyOnChange) notifyDataSetChanged();        
240     }
241
242     /**
243      * {@inheritDoc}
244      */
245     @Override
246     public void notifyDataSetChanged() {
247         super.notifyDataSetChanged();
248         mNotifyOnChange = true;
249     }
250
251     /**
252      * Control whether methods that change the list ({@link #add},
253      * {@link #insert}, {@link #remove}, {@link #clear}) automatically call
254      * {@link #notifyDataSetChanged}.  If set to false, caller must
255      * manually call notifyDataSetChanged() to have the changes
256      * reflected in the attached view.
257      *
258      * The default is true, and calling notifyDataSetChanged()
259      * resets the flag to true.
260      *
261      * @param notifyOnChange if true, modifications to the list will
262      *                       automatically call {@link
263      *                       #notifyDataSetChanged}
264      */
265     public void setNotifyOnChange(boolean notifyOnChange) {
266         mNotifyOnChange = notifyOnChange;
267     }
268
269     private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
270         mContext = context;
271         mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
272         mResource = mDropDownResource = resource;
273         mObjects = objects;
274         mFieldId = textViewResourceId;
275     }
276
277     /**
278      * Returns the context associated with this array adapter. The context is used
279      * to create views from the resource passed to the constructor.
280      *
281      * @return The Context associated with this adapter.
282      */
283     public Context getContext() {
284         return mContext;
285     }
286
287     /**
288      * {@inheritDoc}
289      */
290     public int getCount() {
291         return mObjects.size();
292     }
293
294     /**
295      * {@inheritDoc}
296      */
297     public T getItem(int position) {
298         return mObjects.get(position);
299     }
300
301     /**
302      * Returns the position of the specified item in the array.
303      *
304      * @param item The item to retrieve the position of.
305      *
306      * @return The position of the specified item.
307      */
308     public int getPosition(T item) {
309         return mObjects.indexOf(item);
310     }
311
312     /**
313      * {@inheritDoc}
314      */
315     public long getItemId(int position) {
316         return position;
317     }
318
319     /**
320      * {@inheritDoc}
321      */
322     public View getView(int position, View convertView, ViewGroup parent) {
323         return createViewFromResource(position, convertView, parent, mResource);
324     }
325
326     private View createViewFromResource(int position, View convertView, ViewGroup parent,
327             int resource) {
328         View view;
329         TextView text;
330
331         if (convertView == null) {
332             view = mInflater.inflate(resource, parent, false);
333         } else {
334             view = convertView;
335         }
336
337         try {
338             if (mFieldId == 0) {
339                 //  If no custom field is assigned, assume the whole resource is a TextView
340                 text = (TextView) view;
341             } else {
342                 //  Otherwise, find the TextView field within the layout
343                 text = (TextView) view.findViewById(mFieldId);
344             }
345         } catch (ClassCastException e) {
346             Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
347             throw new IllegalStateException(
348                     "ArrayAdapter requires the resource ID to be a TextView", e);
349         }
350
351         T item = getItem(position);
352         if (item instanceof CharSequence) {
353             text.setText((CharSequence)item);
354         } else {
355             text.setText(item.toString());
356         }
357
358         return view;
359     }
360
361     /**
362      * <p>Sets the layout resource to create the drop down views.</p>
363      *
364      * @param resource the layout resource defining the drop down views
365      * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
366      */
367     public void setDropDownViewResource(int resource) {
368         this.mDropDownResource = resource;
369     }
370
371     /**
372      * {@inheritDoc}
373      */
374     @Override
375     public View getDropDownView(int position, View convertView, ViewGroup parent) {
376         return createViewFromResource(position, convertView, parent, mDropDownResource);
377     }
378
379     /**
380      * Creates a new ArrayAdapter from external resources. The content of the array is
381      * obtained through {@link android.content.res.Resources#getTextArray(int)}.
382      *
383      * @param context The application's environment.
384      * @param textArrayResId The identifier of the array to use as the data source.
385      * @param textViewResId The identifier of the layout used to create views.
386      *
387      * @return An ArrayAdapter<CharSequence>.
388      */
389     public static ArrayAdapter<CharSequence> createFromResource(Context context,
390             int textArrayResId, int textViewResId) {
391         CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
392         return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
393     }
394
395     /**
396      * {@inheritDoc}
397      */
398     public Filter getFilter() {
399         if (mFilter == null) {
400             mFilter = new ArrayFilter();
401         }
402         return mFilter;
403     }
404
405     /**
406      * <p>An array filter constrains the content of the array adapter with
407      * a prefix. Each item that does not start with the supplied prefix
408      * is removed from the list.</p>
409      */
410     private class ArrayFilter extends Filter {
411         @Override
412         protected FilterResults performFiltering(CharSequence prefix) {
413             FilterResults results = new FilterResults();
414
415             if (mOriginalValues == null) {
416                 synchronized (mLock) {
417                     mOriginalValues = new ArrayList<T>(mObjects);
418                 }
419             }
420
421             if (prefix == null || prefix.length() == 0) {
422                 synchronized (mLock) {
423                     ArrayList<T> list = new ArrayList<T>(mOriginalValues);
424                     results.values = list;
425                     results.count = list.size();
426                 }
427             } else {
428                 String prefixString = prefix.toString().toLowerCase();
429
430                 final ArrayList<T> values = mOriginalValues;
431                 final int count = values.size();
432
433                 final ArrayList<T> newValues = new ArrayList<T>(count);
434
435                 for (int i = 0; i < count; i++) {
436                     final T value = values.get(i);
437                     final String valueText = value.toString().toLowerCase();
438
439                     // First match against the whole, non-splitted value
440                     if (valueText.startsWith(prefixString)) {
441                         newValues.add(value);
442                     } else {
443                         final String[] words = valueText.split(" ");
444                         final int wordCount = words.length;
445
446                         for (int k = 0; k < wordCount; k++) {
447                             if (words[k].startsWith(prefixString)) {
448                                 newValues.add(value);
449                                 break;
450                             }
451                         }
452                     }
453                 }
454
455                 results.values = newValues;
456                 results.count = newValues.size();
457             }
458
459             return results;
460         }
461
462         @Override
463         protected void publishResults(CharSequence constraint, FilterResults results) {
464             //noinspection unchecked
465             mObjects = (List<T>) results.values;
466             if (results.count > 0) {
467                 notifyDataSetChanged();
468             } else {
469                 notifyDataSetInvalidated();
470             }
471         }
472     }
473 }