I have an AutoCompleteTextView with a custom ArrayAdapter that uses ArrayList<Product>
.
I've came to the conclusion that a custom ArrayAdapter of an AutoCompleteTextView must implements Filterable
and you have to make your own Filtering.
From this SO-question & accepted answer and this example, I have made the following ArrayAdapter:
public class AutoCompleteAdapter extends ArrayAdapter<Product> implements Filterable
{
// Logcat tag
private static final String TAG = "AutoCompleteAdapter";
// The OrderedProductItem we need to get the Filtered ProductNames
OrderedProductItem orderedProductItem;
private Context context;
private ArrayList<Product> productsShown, productsAll;
// Default Constructor for an ArrayAdapter
public AutoCompleteAdapter(Context c, int layoutId, ArrayList<Product> objects, OrderedProductItem opi){
// Though we don't use the Layout-ResourceID , we still need it for the super
super(c, layoutId, objects);
L.Log(TAG, "AutoCompleteAdapter Constructor", LogType.VERBOSE);
// ArrayAdapter's setNotifyOnChange is true by default,
// but I set it nonetheless, just in case
setNotifyOnChange(true);
context = c;
replaceList(objects, true);
orderedProductItem = opi;
}
// Setup the ListItem's UI-elements
@Override
public View getView(int position, View convertView, ViewGroup parent){
return createTextViewAsItem(position);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent){
return createTextViewAsItem(position);
}
// To prevent repetition, we have this private method
private TextView createTextViewAsItem(int position){
TextView label = new TextView(context);
String name = "";
if(productsShown != null && productsShown.size() > 0 && position >= 0 && position < productsShown.size() - 1)
name = productsShown.get(position).getName();
label.setText(name);
return label;
}
// Replace the List
// When the boolean is set, we replace this ArrayAdapter's List entirely,
// instead of just the filtering
@SuppressWarnings("unchecked")
public void replaceList(ArrayList<Product> p, boolean replaceInitialList){
if(p != null && p.size() > 0){
productsShown = p;
if(replaceInitialList)
productsAll = (ArrayList<Product>)productsShown.clone();
notifyDataSetChanged();
}
}
// Since we are using an AutoCompleteTextView, the Filtering has been reset and we need to apply this ourselves..
Filter filter = new Filter(){
@Override
public String convertResultToString(Object resultValue){
return ((Product)resultValue).getName();
}
@Override
protected FilterResults performFiltering(CharSequence constraint){
FilterResults filterResults = new FilterResults();
if(productsAll != null){
// If no constraint is given, return the whole list
if(constraint == null){
filterResults.values = productsAll;
filterResults.count = productsAll.size();
}
else if(V.notNull(constraint.toString(), true)){
L.Log(TAG, "performFiltering: " + constraint.toString(), LogType.VERBOSE);
ArrayList<Product> suggestions = new ArrayList<Product>();
if(p.size() > 0)
for(Product p : productsAll)
if(p.getName().toLowerCase(Locale.ENGLISH).contains(constraint.toString().toLowerCase(Locale.ENGLISH)))
suggestions.add(p);
filterResults.values = suggestions;
filterResults.count = suggestions.size();
}
}
return filterResults;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if(results != null && results.count > 0)
replaceList((ArrayList<Product>)results.values, false);
}
};
@Override
public Filter getFilter(){
return filter;
}
}
Everything works perfectly. However, since I have a list of around 1250 Products and these are all looped every time the User changes his input in the AutoCompleteTextView, including the creation of two new instantiations (FilterResults and ArrayList), I was wondering if there is a better solution for this without having to loop though everything on every user input change.
If there isn't I just keep this. I was just wondering since with an AutoCompleteTextView containing around 1250 objects, with a custom ArrayAdapter (including custom Filtering) and a custom TextWatcher, it isn't that good for the performance. Especially since this AutoCompleteTextView is used inside the item of a ListView. Which means I have an AutoCompleteTextView for every item (potentially ranging from ~ 5 to 50, with an average of around 15).