blob: de9f76d6eea17b4cc7a1a2e7fb13317a3d321608 [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;
Artur Satayevad9254c2019-12-10 17:47:54 +000024import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080026import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.util.Log;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080028import android.view.ContextThemeWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32
33import java.util.ArrayList;
34import java.util.Arrays;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +020035import java.util.Collection;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070036import java.util.Collections;
Gilles Debunnebe2c4f92011-01-17 15:14:32 -080037import java.util.Comparator;
38import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40/**
Joe Fernandezd7ef95a2017-04-24 13:50:13 -070041 * You can use this adapter to provide views for an {@link AdapterView},
42 * Returns a view for each object in a collection of data objects you
43 * provide, and can be used with list-based user interface widgets such as
44 * {@link ListView} or {@link Spinner}.
45 * <p>
46 * By default, the array adapter creates a view by calling {@link Object#toString()} on each
47 * data object in the collection you provide, and places the result in a TextView.
48 * You may also customize what type of view is used for the data object in the collection.
49 * To customize what type of view is used for the data object,
50 * override {@link #getView(int, View, ViewGroup)}
51 * and inflate a view resource.
Joe Fernandezd7ef95a2017-04-24 13:50:13 -070052 * </p>
53 * <p>
54 * For an example of using an array adapter with a ListView, see the
55 * <a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">
56 * Adapter Views</a> guide.
57 * </p>
58 * <p>
59 * For an example of using an array adapter with a Spinner, see the
60 * <a href="{@docRoot}guide/topics/ui/controls/spinner.html">Spinners</a> guide.
61 * </p>
62 * <p class="note"><strong>Note:</strong>
63 * If you are considering using array adapter with a ListView, consider using
64 * {@link android.support.v7.widget.RecyclerView} instead.
65 * RecyclerView offers similar features with better performance and more flexibility than
66 * ListView provides.
67 * See the
68 * <a href="https://developer.android.com/guide/topics/ui/layout/recyclerview.html">
69 * Recycler View</a> guide.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 */
Alan Viverette2add9bc2015-06-02 14:54:40 -070071public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 * Lock used to modify the content of {@link #mObjects}. Any write operation
74 * performed on the array should be synchronized on this lock. This lock is also
75 * used by the filter (see {@link #getFilter()} to make a synchronized copy of
76 * the original array of data.
77 */
Mathew Inwood978c6e22018-08-21 15:58:55 +010078 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 private final Object mLock = new Object();
80
Alan Viverette7d5967e2015-06-03 16:27:36 -070081 private final LayoutInflater mInflater;
82
Alan Viverette9e623b62016-04-01 10:11:25 -040083 private final Context mContext;
Alan Viverette7d5967e2015-06-03 16:27:36 -070084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 /**
86 * The resource indicating what views to inflate to display the content of this
87 * array adapter.
88 */
Alan Viverette9e623b62016-04-01 10:11:25 -040089 private final int mResource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
91 /**
92 * The resource indicating what views to inflate to display the content of this
93 * array adapter in a drop down widget.
94 */
95 private int mDropDownResource;
96
97 /**
Alan Viverette9e623b62016-04-01 10:11:25 -040098 * Contains the list of objects that represent the data of this ArrayAdapter.
99 * The content of this list is referred to as "the array" in the documentation.
100 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100101 @UnsupportedAppUsage
Alan Viverette9e623b62016-04-01 10:11:25 -0400102 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.
Mathew Inwood978c6e22018-08-21 15:58:55 +0100124 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private ArrayList<T> mOriginalValues;
126 private ArrayFilter mFilter;
127
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800128 /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
129 private LayoutInflater mDropDownInflater;
130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 /**
132 * Constructor
133 *
134 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800135 * @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 -0800136 * instantiating views.
137 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400138 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) {
139 this(context, resource, 0, new ArrayList<>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 }
141
142 /**
143 * Constructor
144 *
145 * @param context The current context.
146 * @param resource The resource ID for a layout file containing a layout to use when
147 * instantiating views.
148 * @param textViewResourceId The id of the TextView within the layout resource to be populated
149 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400150 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
151 @IdRes int textViewResourceId) {
152 this(context, resource, textViewResourceId, new ArrayList<>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154
155 /**
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700156 * Constructor. This constructor will result in the underlying data collection being
157 * immutable, so methods such as {@link #clear()} will throw an exception.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 *
159 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800160 * @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 -0800161 * instantiating views.
162 * @param objects The objects to represent in the ListView.
163 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400164 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800165 this(context, resource, 0, Arrays.asList(objects));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 }
167
168 /**
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700169 * Constructor. This constructor will result in the underlying data collection being
170 * immutable, so methods such as {@link #clear()} will throw an exception.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 *
172 * @param context The current context.
173 * @param resource The resource ID for a layout file containing a layout to use when
174 * instantiating views.
175 * @param textViewResourceId The id of the TextView within the layout resource to be populated
176 * @param objects The objects to represent in the ListView.
177 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400178 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
179 @IdRes int textViewResourceId, @NonNull T[] objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800180 this(context, resource, textViewResourceId, Arrays.asList(objects));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 }
182
183 /**
184 * Constructor
185 *
186 * @param context The current context.
Romain Guy9c5d1b12013-01-09 20:16:37 -0800187 * @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 -0800188 * instantiating views.
189 * @param objects The objects to represent in the ListView.
190 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400191 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
192 @NonNull List<T> objects) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800193 this(context, resource, 0, objects);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
195
196 /**
197 * Constructor
198 *
199 * @param context The current context.
200 * @param resource The resource ID for a layout file containing a layout to use when
201 * instantiating views.
202 * @param textViewResourceId The id of the TextView within the layout resource to be populated
203 * @param objects The objects to represent in the ListView.
204 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400205 public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
206 @IdRes int textViewResourceId, @NonNull List<T> objects) {
Felipe Leme7e4c2052017-04-18 09:45:58 -0700207 this(context, resource, textViewResourceId, objects, false);
208 }
209
210 private ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
211 @IdRes int textViewResourceId, @NonNull List<T> objects, boolean objsFromResources) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800212 mContext = context;
213 mInflater = LayoutInflater.from(context);
214 mResource = mDropDownResource = resource;
215 mObjects = objects;
Felipe Leme7e4c2052017-04-18 09:45:58 -0700216 mObjectsFromResources = objsFromResources;
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800217 mFieldId = textViewResourceId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219
220 /**
221 * Adds the specified object at the end of the array.
222 *
223 * @param object The object to add at the end of the array.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700224 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400226 public void add(@Nullable T object) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800227 synchronized (mLock) {
228 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 mOriginalValues.add(object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800230 } else {
231 mObjects.add(object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700233 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800235 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
237
238 /**
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200239 * Adds the specified Collection at the end of the array.
240 *
241 * @param collection The Collection to add at the end of the array.
Alan Viverette9e623b62016-04-01 10:11:25 -0400242 * @throws UnsupportedOperationException if the <tt>addAll</tt> operation
243 * is not supported by this list
244 * @throws ClassCastException if the class of an element of the specified
245 * collection prevents it from being added to this list
246 * @throws NullPointerException if the specified collection contains one
247 * or more null elements and this list does not permit null
248 * elements, or if the specified collection is null
249 * @throws IllegalArgumentException if some property of an element of the
250 * specified collection prevents it from being added to this list
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200251 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400252 public void addAll(@NonNull Collection<? extends T> collection) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800253 synchronized (mLock) {
254 if (mOriginalValues != null) {
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200255 mOriginalValues.addAll(collection);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800256 } else {
257 mObjects.addAll(collection);
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200258 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700259 mObjectsFromResources = false;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200260 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800261 if (mNotifyOnChange) notifyDataSetChanged();
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200262 }
263
264 /**
265 * Adds the specified items at the end of the array.
266 *
267 * @param items The items to add at the end of the array.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700268 * @throws UnsupportedOperationException if the underlying data collection is immutable
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200269 */
270 public void addAll(T ... items) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800271 synchronized (mLock) {
272 if (mOriginalValues != null) {
Romain Guy95a78c32011-08-15 15:36:30 -0700273 Collections.addAll(mOriginalValues, items);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800274 } else {
Romain Guy95a78c32011-08-15 15:36:30 -0700275 Collections.addAll(mObjects, items);
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200276 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700277 mObjectsFromResources = false;
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200278 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800279 if (mNotifyOnChange) notifyDataSetChanged();
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200280 }
281
282 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 * Inserts the specified object at the specified index in the array.
284 *
285 * @param object The object to insert into the array.
286 * @param index The index at which the object must be inserted.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700287 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400289 public void insert(@Nullable T object, int index) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800290 synchronized (mLock) {
291 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 mOriginalValues.add(index, object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800293 } else {
294 mObjects.add(index, object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700296 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800298 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 }
300
301 /**
302 * Removes the specified object from the array.
303 *
304 * @param object The object to remove.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700305 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400307 public void remove(@Nullable T object) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800308 synchronized (mLock) {
309 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 mOriginalValues.remove(object);
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800311 } else {
312 mObjects.remove(object);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700314 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
316 if (mNotifyOnChange) notifyDataSetChanged();
317 }
318
319 /**
320 * Remove all elements from the list.
Chet Haaseb7c6a5d2017-08-09 12:55:00 -0700321 *
322 * @throws UnsupportedOperationException if the underlying data collection is immutable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 */
324 public void clear() {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800325 synchronized (mLock) {
326 if (mOriginalValues != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 mOriginalValues.clear();
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800328 } else {
329 mObjects.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
Felipe Leme7e4c2052017-04-18 09:45:58 -0700331 mObjectsFromResources = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 }
333 if (mNotifyOnChange) notifyDataSetChanged();
334 }
335
336 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 * Sorts the content of this adapter using the specified comparator.
338 *
339 * @param comparator The comparator used to sort the objects contained
340 * in this adapter.
341 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400342 public void sort(@NonNull Comparator<? super T> comparator) {
Gilles Debunnebe2c4f92011-01-17 15:14:32 -0800343 synchronized (mLock) {
344 if (mOriginalValues != null) {
345 Collections.sort(mOriginalValues, comparator);
346 } else {
347 Collections.sort(mObjects, comparator);
348 }
349 }
Christian Mehlmauer8c582ef2010-06-11 22:28:38 +0200350 if (mNotifyOnChange) notifyDataSetChanged();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700351 }
352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 @Override
354 public void notifyDataSetChanged() {
355 super.notifyDataSetChanged();
356 mNotifyOnChange = true;
357 }
358
359 /**
Yigit Boyar42c69b92016-08-19 10:15:52 -0700360 * Control whether methods that change the list ({@link #add}, {@link #addAll(Collection)},
361 * {@link #addAll(Object[])}, {@link #insert}, {@link #remove}, {@link #clear},
362 * {@link #sort(Comparator)}) automatically call {@link #notifyDataSetChanged}. If set to
363 * false, caller must manually call notifyDataSetChanged() to have the changes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 * reflected in the attached view.
365 *
366 * The default is true, and calling notifyDataSetChanged()
367 * resets the flag to true.
368 *
369 * @param notifyOnChange if true, modifications to the list will
370 * automatically call {@link
371 * #notifyDataSetChanged}
372 */
373 public void setNotifyOnChange(boolean notifyOnChange) {
374 mNotifyOnChange = notifyOnChange;
375 }
376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 /**
378 * Returns the context associated with this array adapter. The context is used
379 * to create views from the resource passed to the constructor.
380 *
381 * @return The Context associated with this adapter.
382 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400383 public @NonNull Context getContext() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 return mContext;
385 }
386
Alan Viverette9e623b62016-04-01 10:11:25 -0400387 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 public int getCount() {
389 return mObjects.size();
390 }
391
Alan Viverette9e623b62016-04-01 10:11:25 -0400392 @Override
393 public @Nullable T getItem(int position) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return mObjects.get(position);
395 }
396
397 /**
398 * Returns the position of the specified item in the array.
399 *
400 * @param item The item to retrieve the position of.
401 *
402 * @return The position of the specified item.
403 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400404 public int getPosition(@Nullable T item) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 return mObjects.indexOf(item);
406 }
407
Alan Viverette9e623b62016-04-01 10:11:25 -0400408 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 public long getItemId(int position) {
410 return position;
411 }
412
Alan Viverette9e623b62016-04-01 10:11:25 -0400413 @Override
414 public @NonNull View getView(int position, @Nullable View convertView,
415 @NonNull ViewGroup parent) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800416 return createViewFromResource(mInflater, position, convertView, parent, mResource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 }
418
Alan Viverette9e623b62016-04-01 10:11:25 -0400419 private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position,
420 @Nullable View convertView, @NonNull ViewGroup parent, int resource) {
421 final View view;
422 final TextView text;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423
424 if (convertView == null) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800425 view = inflater.inflate(resource, parent, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 } else {
427 view = convertView;
428 }
429
430 try {
431 if (mFieldId == 0) {
432 // If no custom field is assigned, assume the whole resource is a TextView
433 text = (TextView) view;
434 } else {
435 // Otherwise, find the TextView field within the layout
Alan Viverette8e1a7292017-02-27 10:57:58 -0500436 text = view.findViewById(mFieldId);
Alan Viverette9e623b62016-04-01 10:11:25 -0400437
438 if (text == null) {
439 throw new RuntimeException("Failed to find view with ID "
440 + mContext.getResources().getResourceName(mFieldId)
441 + " in item layout");
442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 }
444 } catch (ClassCastException e) {
445 Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
446 throw new IllegalStateException(
447 "ArrayAdapter requires the resource ID to be a TextView", e);
448 }
449
Alan Viverette9e623b62016-04-01 10:11:25 -0400450 final T item = getItem(position);
Daisuke Miyakawab5d91322009-07-09 14:26:56 +0900451 if (item instanceof CharSequence) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400452 text.setText((CharSequence) item);
Daisuke Miyakawab5d91322009-07-09 14:26:56 +0900453 } else {
454 text.setText(item.toString());
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456
457 return view;
458 }
459
460 /**
461 * <p>Sets the layout resource to create the drop down views.</p>
462 *
463 * @param resource the layout resource defining the drop down views
464 * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
465 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700466 public void setDropDownViewResource(@LayoutRes int resource) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 this.mDropDownResource = resource;
468 }
469
470 /**
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800471 * Sets the {@link Resources.Theme} against which drop-down views are
472 * inflated.
473 * <p>
474 * By default, drop-down views are inflated against the theme of the
475 * {@link Context} passed to the adapter's constructor.
476 *
477 * @param theme the theme against which to inflate drop-down views or
478 * {@code null} to use the theme from the adapter's context
479 * @see #getDropDownView(int, View, ViewGroup)
480 */
481 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400482 public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800483 if (theme == null) {
484 mDropDownInflater = null;
485 } else if (theme == mInflater.getContext().getTheme()) {
486 mDropDownInflater = mInflater;
487 } else {
488 final Context context = new ContextThemeWrapper(mContext, theme);
489 mDropDownInflater = LayoutInflater.from(context);
490 }
491 }
492
493 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400494 public @Nullable Resources.Theme getDropDownViewTheme() {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800495 return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
496 }
497
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 @Override
Alan Viverette9e623b62016-04-01 10:11:25 -0400499 public View getDropDownView(int position, @Nullable View convertView,
500 @NonNull ViewGroup parent) {
Alan Viveretteb9ead4a2015-01-14 10:43:31 -0800501 final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
502 return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 }
504
505 /**
506 * Creates a new ArrayAdapter from external resources. The content of the array is
507 * obtained through {@link android.content.res.Resources#getTextArray(int)}.
508 *
509 * @param context The application's environment.
510 * @param textArrayResId The identifier of the array to use as the data source.
511 * @param textViewResId The identifier of the layout used to create views.
512 *
513 * @return An ArrayAdapter<CharSequence>.
514 */
Alan Viverette9e623b62016-04-01 10:11:25 -0400515 public static @NonNull ArrayAdapter<CharSequence> createFromResource(@NonNull Context context,
Tor Norbye7b9c9122013-05-30 16:48:33 -0700516 @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400517 final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
Felipe Leme7e4c2052017-04-18 09:45:58 -0700518 return new ArrayAdapter<>(context, textViewResId, 0, Arrays.asList(strings), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 }
520
Alan Viverette9e623b62016-04-01 10:11:25 -0400521 @Override
522 public @NonNull Filter getFilter() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 if (mFilter == null) {
524 mFilter = new ArrayFilter();
525 }
526 return mFilter;
527 }
528
529 /**
Felipe Leme7e4c2052017-04-18 09:45:58 -0700530 * {@inheritDoc}
531 *
532 * @return values from the string array used by {@link #createFromResource(Context, int, int)},
533 * or {@code null} if object was created otherwsie or if contents were dynamically changed after
534 * creation.
535 */
536 @Override
537 public CharSequence[] getAutofillOptions() {
Felipe Leme49a38c62017-06-13 12:51:43 -0700538 // First check if app developer explicitly set them.
539 final CharSequence[] explicitOptions = super.getAutofillOptions();
540 if (explicitOptions != null) {
541 return explicitOptions;
542 }
543
544 // Otherwise, only return options that came from static resources.
Felipe Leme7e4c2052017-04-18 09:45:58 -0700545 if (!mObjectsFromResources || mObjects == null || mObjects.isEmpty()) {
546 return null;
547 }
548 final int size = mObjects.size();
549 final CharSequence[] options = new CharSequence[size];
550 mObjects.toArray(options);
551 return options;
552 }
553
554 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 * <p>An array filter constrains the content of the array adapter with
556 * a prefix. Each item that does not start with the supplied prefix
557 * is removed from the list.</p>
558 */
559 private class ArrayFilter extends Filter {
560 @Override
561 protected FilterResults performFiltering(CharSequence prefix) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400562 final FilterResults results = new FilterResults();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563
564 if (mOriginalValues == null) {
565 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400566 mOriginalValues = new ArrayList<>(mObjects);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568 }
569
570 if (prefix == null || prefix.length() == 0) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400571 final ArrayList<T> list;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400573 list = new ArrayList<>(mOriginalValues);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 }
Romain Guy95a78c32011-08-15 15:36:30 -0700575 results.values = list;
576 results.count = list.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 } else {
Alan Viverette9e623b62016-04-01 10:11:25 -0400578 final String prefixString = prefix.toString().toLowerCase();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579
Alan Viverette9e623b62016-04-01 10:11:25 -0400580 final ArrayList<T> values;
Romain Guy95a78c32011-08-15 15:36:30 -0700581 synchronized (mLock) {
Alan Viverette9e623b62016-04-01 10:11:25 -0400582 values = new ArrayList<>(mOriginalValues);
Romain Guy95a78c32011-08-15 15:36:30 -0700583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584
Romain Guy95a78c32011-08-15 15:36:30 -0700585 final int count = values.size();
Alan Viverette9e623b62016-04-01 10:11:25 -0400586 final ArrayList<T> newValues = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587
588 for (int i = 0; i < count; i++) {
589 final T value = values.get(i);
590 final String valueText = value.toString().toLowerCase();
591
592 // First match against the whole, non-splitted value
593 if (valueText.startsWith(prefixString)) {
594 newValues.add(value);
595 } else {
596 final String[] words = valueText.split(" ");
Alan Viverette9e623b62016-04-01 10:11:25 -0400597 for (String word : words) {
598 if (word.startsWith(prefixString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 newValues.add(value);
600 break;
601 }
602 }
603 }
604 }
605
606 results.values = newValues;
607 results.count = newValues.size();
608 }
609
610 return results;
611 }
612
613 @Override
614 protected void publishResults(CharSequence constraint, FilterResults results) {
615 //noinspection unchecked
616 mObjects = (List<T>) results.values;
617 if (results.count > 0) {
618 notifyDataSetChanged();
619 } else {
620 notifyDataSetInvalidated();
621 }
622 }
623 }
624}