blob: f18f2172b455ce58f7486d44b947c564da467f4d [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
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.ArrayRes;
20import android.annotation.IdRes;
21import android.annotation.LayoutRes;
Tor Norbye417ee5b2015-03-10 20:57:37 -070022import android.annotation.NonNull;
Alan Viverette9e623b62016-04-01 10:11:25 -040023import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080025import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.util.Log;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080027import android.view.ContextThemeWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31
32import java.util.ArrayList;
33import java.util.Arrays;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +020034import java.util.Collection;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070035import java.util.Collections;
Gilles Debunnebe2c4f92011-01-17 15:14:32 -080036import java.util.Comparator;
37import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
39/**
Joe Fernandezd7ef95a2017-04-24 13:50:13 -070040 * You can use this adapter to provide views for an {@link AdapterView},
41 * Returns a view for each object in a collection of data objects you
42 * provide, and can be used with list-based user interface widgets such as
43 * {@link ListView} or {@link Spinner}.
44 * <p>
45 * By default, the array adapter creates a view by calling {@link Object#toString()} on each
46 * data object in the collection you provide, and places the result in a TextView.
47 * You may also customize what type of view is used for the data object in the collection.
48 * To customize what type of view is used for the data object,
49 * override {@link #getView(int, View, ViewGroup)}
50 * and inflate a view resource.
51 * For a code example, see
52 * the <a href="https://developer.android.com/samples/CustomChoiceList/index.html">
53 * CustomChoiceList</a> sample.
54 * </p>
55 * <p>
56 * For an example of using an array adapter with a ListView, see the
57 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">
58 * Adapter Views</a> guide.
59 * </p>
60 * <p>
61 * For an example of using an array adapter with a Spinner, see the
62 * <a href="{@docRoot}guide/topics/ui/controls/spinner.html">Spinners</a> guide.
63 * </p>
64 * <p class="note"><strong>Note:</strong>
65 * If you are considering using array adapter with a ListView, consider using
66 * {@link android.support.v7.widget.RecyclerView} instead.
67 * RecyclerView offers similar features with better performance and more flexibility than
68 * ListView provides.
69 * See the
70 * <a href="https://developer.android.com/guide/topics/ui/layout/recyclerview.html">
71 * Recycler View</a> guide.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 */
Alan Viverette2add9bc2015-06-02 14:54:40 -070073public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 * Lock used to modify the content of {@link #mObjects}. Any write operation
76 * performed on the array should be synchronized on this lock. This lock is also
77 * used by the filter (see {@link #getFilter()} to make a synchronized copy of
78 * the original array of data.
79 */
80 private final Object mLock = new Object();
81
Alan Viverette7d5967e2015-06-03 16:27:36 -070082 private final LayoutInflater mInflater;
83
Alan Viverette9e623b62016-04-01 10:11:25 -040084 private final Context mContext;
Alan Viverette7d5967e2015-06-03 16:27:36 -070085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 /**
87 * The resource indicating what views to inflate to display the content of this
88 * array adapter.
89 */
Alan Viverette9e623b62016-04-01 10:11:25 -040090 private final int mResource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 /**
93 * The resource indicating what views to inflate to display the content of this
94 * array adapter in a drop down widget.
95 */
96 private int mDropDownResource;
97
98 /**
Alan Viverette9e623b62016-04-01 10:11:25 -040099 * Contains the list of objects that represent the data of this ArrayAdapter.
100 * The content of this list is referred to as "the array" in the documentation.
101 */
102 private List<T> mObjects;
103
104 /**
Felipe Leme7e4c2052017-04-18 09:45:58 -0700105 * Indicates whether the contents of {@link #mObjects} came from static resources.
106 */
107 private boolean mObjectsFromResources;
108
109 /**
Alan Viverette9e623b62016-04-01 10:11:25 -0400110 * If the inflated resource is not a TextView, {@code mFieldId} is used to find
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 * a TextView inside the inflated views hierarchy. This field must contain the
112 * identifier that matches the one defined in the resource file.
113 */
114 private int mFieldId = 0;
115
116 /**
117 * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
118 * {@link #mObjects} is modified.
119 */
120 private boolean mNotifyOnChange = true;
121
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800122 // A copy of the original mObjects array, initialized from and then used instead as soon as
123 // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 private ArrayList<T> mOriginalValues;
125 private ArrayFilter mFilter;
126
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800127 /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
128 private LayoutInflater mDropDownInflater;
129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 /**
131 * Constructor
132 *
133 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800134 * @param resource The resource ID for a layout file containing a TextView to use when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 * instantiating views.
136 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400137 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) {
138 this(context, resource, 0, new ArrayList<>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 }
140
141 /**
142 * Constructor
143 *
144 * @param context The current context.
145 * @param resource The resource ID for a layout file containing a layout to use when
146 * instantiating views.
147 * @param textViewResourceId The id of the TextView within the layout resource to be populated
148 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400149 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
150 @IdRes int textViewResourceId) {
151 this(context, resource, textViewResourceId, new ArrayList<>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 }
153
154 /**
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700155 * Constructor. This constructor will result in the underlying data collection being
156 * immutable, so methods such as {@link #clear()} will throw an exception.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 *
158 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800159 * @param resource The resource ID for a layout file containing a TextView to use when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 * instantiating views.
161 * @param objects The objects to represent in the ListView.
162 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400163 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800164 this(context, resource, 0, Arrays.asList(objects));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 }
166
167 /**
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700168 * Constructor. This constructor will result in the underlying data collection being
169 * immutable, so methods such as {@link #clear()} will throw an exception.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 *
171 * @param context The current context.
172 * @param resource The resource ID for a layout file containing a layout to use when
173 * instantiating views.
174 * @param textViewResourceId The id of the TextView within the layout resource to be populated
175 * @param objects The objects to represent in the ListView.
176 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400177 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
178 @IdRes int textViewResourceId, @NonNull T[] objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800179 this(context, resource, textViewResourceId, Arrays.asList(objects));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 }
181
182 /**
183 * Constructor
184 *
185 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800186 * @param resource The resource ID for a layout file containing a TextView to use when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 * instantiating views.
188 * @param objects The objects to represent in the ListView.
189 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400190 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
191 @NonNull List<T> objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800192 this(context, resource, 0, objects);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194
195 /**
196 * Constructor
197 *
198 * @param context The current context.
199 * @param resource The resource ID for a layout file containing a layout to use when
200 * instantiating views.
201 * @param textViewResourceId The id of the TextView within the layout resource to be populated
202 * @param objects The objects to represent in the ListView.
203 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400204 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
205 @IdRes int textViewResourceId, @NonNull List<T> objects) {
Felipe Leme7e4c2052017-04-18 09:45:58 -0700206 this(context, resource, textViewResourceId, objects, false);
207 }
208
209 private ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
210 @IdRes int textViewResourceId, @NonNull List<T> objects, boolean objsFromResources) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800211 mContext = context;
212 mInflater = LayoutInflater.from(context);
213 mResource = mDropDownResource = resource;
214 mObjects = objects;
Felipe Leme7e4c2052017-04-18 09:45:58 -0700215 mObjectsFromResources = objsFromResources;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800216 mFieldId = textViewResourceId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218
219 /**
220 * Adds the specified object at the end of the array.
221 *
222 * @param object The object to add at the end of the array.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700223 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400225 public void add(@Nullable T object) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800226 synchronized (mLock) {
227 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 mOriginalValues.add(object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800229 } else {
230 mObjects.add(object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700232 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800234 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 }
236
237 /**
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200238 * Adds the specified Collection at the end of the array.
239 *
240 * @param collection The Collection to add at the end of the array.
Alan Viverette9e623b62016-04-01 10:11:25 -0400241 * @throws UnsupportedOperationException if the <tt>addAll</tt> operation
242 * is not supported by this list
243 * @throws ClassCastException if the class of an element of the specified
244 * collection prevents it from being added to this list
245 * @throws NullPointerException if the specified collection contains one
246 * or more null elements and this list does not permit null
247 * elements, or if the specified collection is null
248 * @throws IllegalArgumentException if some property of an element of the
249 * specified collection prevents it from being added to this list
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200250 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400251 public void addAll(@NonNull Collection<? extends T> collection) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800252 synchronized (mLock) {
253 if (mOriginalValues != null) {
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200254 mOriginalValues.addAll(collection);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800255 } else {
256 mObjects.addAll(collection);
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200257 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700258 mObjectsFromResources = false;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200259 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800260 if (mNotifyOnChange) notifyDataSetChanged();
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200261 }
262
263 /**
264 * Adds the specified items at the end of the array.
265 *
266 * @param items The items to add at the end of the array.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700267 * @throws UnsupportedOperationException if the underlying data collection is immutable
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200268 */
269 public void addAll(T ... items) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800270 synchronized (mLock) {
271 if (mOriginalValues != null) {
Romain Guy95a78c32011-08-15 15:36:30 -0700272 Collections.addAll(mOriginalValues, items);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800273 } else {
Romain Guy95a78c32011-08-15 15:36:30 -0700274 Collections.addAll(mObjects, items);
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200275 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700276 mObjectsFromResources = false;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200277 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800278 if (mNotifyOnChange) notifyDataSetChanged();
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200279 }
280
281 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 * Inserts the specified object at the specified index in the array.
283 *
284 * @param object The object to insert into the array.
285 * @param index The index at which the object must be inserted.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700286 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400288 public void insert(@Nullable T object, int index) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800289 synchronized (mLock) {
290 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 mOriginalValues.add(index, object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800292 } else {
293 mObjects.add(index, object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700295 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800297 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299
300 /**
301 * Removes the specified object from the array.
302 *
303 * @param object The object to remove.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700304 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400306 public void remove(@Nullable T object) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800307 synchronized (mLock) {
308 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 mOriginalValues.remove(object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800310 } else {
311 mObjects.remove(object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700313 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315 if (mNotifyOnChange) notifyDataSetChanged();
316 }
317
318 /**
319 * Remove all elements from the list.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700320 *
321 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 */
323 public void clear() {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800324 synchronized (mLock) {
325 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 mOriginalValues.clear();
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800327 } else {
328 mObjects.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700330 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332 if (mNotifyOnChange) notifyDataSetChanged();
333 }
334
335 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700336 * Sorts the content of this adapter using the specified comparator.
337 *
338 * @param comparator The comparator used to sort the objects contained
339 * in this adapter.
340 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400341 public void sort(@NonNull Comparator<? super T> comparator) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800342 synchronized (mLock) {
343 if (mOriginalValues != null) {
344 Collections.sort(mOriginalValues, comparator);
345 } else {
346 Collections.sort(mObjects, comparator);
347 }
348 }
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200349 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700350 }
351
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 @Override
353 public void notifyDataSetChanged() {
354 super.notifyDataSetChanged();
355 mNotifyOnChange = true;
356 }
357
358 /**
Yigit Boyar42c69b92016-08-19 10:15:52 -0700359 * Control whether methods that change the list ({@link #add}, {@link #addAll(Collection)},
360 * {@link #addAll(Object[])}, {@link #insert}, {@link #remove}, {@link #clear},
361 * {@link #sort(Comparator)}) automatically call {@link #notifyDataSetChanged}. If set to
362 * false, caller must manually call notifyDataSetChanged() to have the changes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 * reflected in the attached view.
364 *
365 * The default is true, and calling notifyDataSetChanged()
366 * resets the flag to true.
367 *
368 * @param notifyOnChange if true, modifications to the list will
369 * automatically call {@link
370 * #notifyDataSetChanged}
371 */
372 public void setNotifyOnChange(boolean notifyOnChange) {
373 mNotifyOnChange = notifyOnChange;
374 }
375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 /**
377 * Returns the context associated with this array adapter. The context is used
378 * to create views from the resource passed to the constructor.
379 *
380 * @return The Context associated with this adapter.
381 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400382 public @NonNull Context getContext() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 return mContext;
384 }
385
Alan Viverette9e623b62016-04-01 10:11:25 -0400386 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 public int getCount() {
388 return mObjects.size();
389 }
390
Alan Viverette9e623b62016-04-01 10:11:25 -0400391 @Override
392 public @Nullable T getItem(int position) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 return mObjects.get(position);
394 }
395
396 /**
397 * Returns the position of the specified item in the array.
398 *
399 * @param item The item to retrieve the position of.
400 *
401 * @return The position of the specified item.
402 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400403 public int getPosition(@Nullable T item) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 return mObjects.indexOf(item);
405 }
406
Alan Viverette9e623b62016-04-01 10:11:25 -0400407 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 public long getItemId(int position) {
409 return position;
410 }
411
Alan Viverette9e623b62016-04-01 10:11:25 -0400412 @Override
413 public @NonNull View getView(int position, @Nullable View convertView,
414 @NonNull ViewGroup parent) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800415 return createViewFromResource(mInflater, position, convertView, parent, mResource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417
Alan Viverette9e623b62016-04-01 10:11:25 -0400418 private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position,
419 @Nullable View convertView, @NonNull ViewGroup parent, int resource) {
420 final View view;
421 final TextView text;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422
423 if (convertView == null) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800424 view = inflater.inflate(resource, parent, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 } else {
426 view = convertView;
427 }
428
429 try {
430 if (mFieldId == 0) {
431 // If no custom field is assigned, assume the whole resource is a TextView
432 text = (TextView) view;
433 } else {
434 // Otherwise, find the TextView field within the layout
Alan Viverette8e1a7292017-02-27 10:57:58 -0500435 text = view.findViewById(mFieldId);
Alan Viverette9e623b62016-04-01 10:11:25 -0400436
437 if (text == null) {
438 throw new RuntimeException("Failed to find view with ID "
439 + mContext.getResources().getResourceName(mFieldId)
440 + " in item layout");
441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 }
443 } catch (ClassCastException e) {
444 Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
445 throw new IllegalStateException(
446 "ArrayAdapter requires the resource ID to be a TextView", e);
447 }
448
Alan Viverette9e623b62016-04-01 10:11:25 -0400449 final T item = getItem(position);
Daisuke Miyakawab5d91322009-07-09 14:26:56 +0900450 if (item instanceof CharSequence) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400451 text.setText((CharSequence) item);
Daisuke Miyakawab5d91322009-07-09 14:26:56 +0900452 } else {
453 text.setText(item.toString());
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455
456 return view;
457 }
458
459 /**
460 * <p>Sets the layout resource to create the drop down views.</p>
461 *
462 * @param resource the layout resource defining the drop down views
463 * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
464 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700465 public void setDropDownViewResource(@LayoutRes int resource) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 this.mDropDownResource = resource;
467 }
468
469 /**
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800470 * Sets the {@link Resources.Theme} against which drop-down views are
471 * inflated.
472 * <p>
473 * By default, drop-down views are inflated against the theme of the
474 * {@link Context} passed to the adapter's constructor.
475 *
476 * @param theme the theme against which to inflate drop-down views or
477 * {@code null} to use the theme from the adapter's context
478 * @see #getDropDownView(int, View, ViewGroup)
479 */
480 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400481 public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800482 if (theme == null) {
483 mDropDownInflater = null;
484 } else if (theme == mInflater.getContext().getTheme()) {
485 mDropDownInflater = mInflater;
486 } else {
487 final Context context = new ContextThemeWrapper(mContext, theme);
488 mDropDownInflater = LayoutInflater.from(context);
489 }
490 }
491
492 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400493 public @Nullable Resources.Theme getDropDownViewTheme() {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800494 return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
495 }
496
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400498 public View getDropDownView(int position, @Nullable View convertView,
499 @NonNull ViewGroup parent) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800500 final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
501 return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 }
503
504 /**
505 * Creates a new ArrayAdapter from external resources. The content of the array is
506 * obtained through {@link android.content.res.Resources#getTextArray(int)}.
507 *
508 * @param context The application's environment.
509 * @param textArrayResId The identifier of the array to use as the data source.
510 * @param textViewResId The identifier of the layout used to create views.
511 *
512 * @return An ArrayAdapter<CharSequence>.
513 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400514 public static @NonNull ArrayAdapter<CharSequence> createFromResource(@NonNull Context context,
Tor Norbye7b9c9122013-05-30 16:48:33 -0700515 @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400516 final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
Felipe Leme7e4c2052017-04-18 09:45:58 -0700517 return new ArrayAdapter<>(context, textViewResId, 0, Arrays.asList(strings), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 }
519
Alan Viverette9e623b62016-04-01 10:11:25 -0400520 @Override
521 public @NonNull Filter getFilter() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 if (mFilter == null) {
523 mFilter = new ArrayFilter();
524 }
525 return mFilter;
526 }
527
528 /**
Felipe Leme7e4c2052017-04-18 09:45:58 -0700529 * {@inheritDoc}
530 *
531 * @return values from the string array used by {@link #createFromResource(Context, int, int)},
532 * or {@code null} if object was created otherwsie or if contents were dynamically changed after
533 * creation.
534 */
535 @Override
536 public CharSequence[] getAutofillOptions() {
Felipe Leme49a38c62017-06-13 12:51:43 -0700537 // First check if app developer explicitly set them.
538 final CharSequence[] explicitOptions = super.getAutofillOptions();
539 if (explicitOptions != null) {
540 return explicitOptions;
541 }
542
543 // Otherwise, only return options that came from static resources.
Felipe Leme7e4c2052017-04-18 09:45:58 -0700544 if (!mObjectsFromResources || mObjects == null || mObjects.isEmpty()) {
545 return null;
546 }
547 final int size = mObjects.size();
548 final CharSequence[] options = new CharSequence[size];
549 mObjects.toArray(options);
550 return options;
551 }
552
553 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 * <p>An array filter constrains the content of the array adapter with
555 * a prefix. Each item that does not start with the supplied prefix
556 * is removed from the list.</p>
557 */
558 private class ArrayFilter extends Filter {
559 @Override
560 protected FilterResults performFiltering(CharSequence prefix) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400561 final FilterResults results = new FilterResults();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562
563 if (mOriginalValues == null) {
564 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400565 mOriginalValues = new ArrayList<>(mObjects);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 }
567 }
568
569 if (prefix == null || prefix.length() == 0) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400570 final ArrayList<T> list;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400572 list = new ArrayList<>(mOriginalValues);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 }
Romain Guy95a78c32011-08-15 15:36:30 -0700574 results.values = list;
575 results.count = list.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 } else {
Alan Viverette9e623b62016-04-01 10:11:25 -0400577 final String prefixString = prefix.toString().toLowerCase();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578
Alan Viverette9e623b62016-04-01 10:11:25 -0400579 final ArrayList<T> values;
Romain Guy95a78c32011-08-15 15:36:30 -0700580 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400581 values = new ArrayList<>(mOriginalValues);
Romain Guy95a78c32011-08-15 15:36:30 -0700582 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583
Romain Guy95a78c32011-08-15 15:36:30 -0700584 final int count = values.size();
Alan Viverette9e623b62016-04-01 10:11:25 -0400585 final ArrayList<T> newValues = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586
587 for (int i = 0; i < count; i++) {
588 final T value = values.get(i);
589 final String valueText = value.toString().toLowerCase();
590
591 // First match against the whole, non-splitted value
592 if (valueText.startsWith(prefixString)) {
593 newValues.add(value);
594 } else {
595 final String[] words = valueText.split(" ");
Alan Viverette9e623b62016-04-01 10:11:25 -0400596 for (String word : words) {
597 if (word.startsWith(prefixString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 newValues.add(value);
599 break;
600 }
601 }
602 }
603 }
604
605 results.values = newValues;
606 results.count = newValues.size();
607 }
608
609 return results;
610 }
611
612 @Override
613 protected void publishResults(CharSequence constraint, FilterResults results) {
614 //noinspection unchecked
615 mObjects = (List<T>) results.values;
616 if (results.count > 0) {
617 notifyDataSetChanged();
618 } else {
619 notifyDataSetInvalidated();
620 }
621 }
622 }
623}