I have added in the Toolbar a SearchView to filter some items.
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu, menu);
MenuItem searchItem = menu.findItem(R.id.search);
SearchView search = (SearchView) searchItem.getActionView();
autoComplete = search.findViewById(androidx.appcompat.R.id.search_src_text);
}
In my MainActivity I get a the list of items an pass it to my adapter:
ItemsArrayAdapter adapter = new ItemsArrayAdapter(this, itemList);
autoComplete.setAdapter(adapter);
And this is my adapter class:
public class ItemsArrayAdapter extends ArrayAdapter<Item> {
private List<Item> itemListFull;
ItemsArrayAdapter(@NonNull Context context, @NonNull List<Item> items) {
super(context, 0, items);
itemListFull = new ArrayList<>(items);
}
@NonNull
@Override
public Filter getFilter() {
return itemFilter;
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
Item item = getItem(position);
if (convertView == null) {
//Inflate view
}
return convertView;
}
private Filter itemFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Item> suggestionList = new ArrayList<>();
if (constraint == null || constraint.length() == 0) {
suggestionList.addAll(itemListFull);
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for (Item item : itemListFull) {
if (item.name.toLowerCase().contains(filterPattern)) {
suggestionList.add(item);
}
}
}
results.values = suggestionList;
results.count = suggestionList.size();
Log.d(TAG, "size: " + suggestionList.size());
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
List<Item> items = (List<Item>) results.values;
if (items != null) {
clear();
addAll(items);
}
notifyDataSetChanged();
}
@Override
public CharSequence convertResultToString(Object resultValue) {
return ((Item) resultValue).name;
}
};
}
The number of items that exist in the list is 26. When I write a letter for example c
from word car
the number of items that is returned is 26, even though the words that contain c
are only 11. When I type the second letter a
the number of items that is returned is 1 (which is correct) and the item is correctly displayed in the view. How to make the SearchView fire and return the correct number of item from the first time? Thanks.
That search_src_text
in SearchView
is a descendant of AutoCompleteTextView
, and it inherits most of its behavior from that class. AutoCompleteTextView
has a default threshold of 2 characters. That is, it won't start filtering until you've input at least 2 characters, which explains why you were seeing the complete list when entering only one. We can fix that by lowering the threshold to 1.
AutoCompleteTextView
has the setThreshold()
method, so we could simply call that with an argument of 1. However, as you noted, this causes a can only be called from within the same library group
lint warning, because the specific subclass that the androidx SearchView
uses is marked @hide
in the source. That can easily be suppressed with @SuppressLint("RestrictedApi")
, but if you'd rather not use a restricted API, there is another way.
We can set that threshold via the android:completionThreshold
XML attribute specified in a sub-style of the default AutoCompleteTextView
style that we then set as the autoCompleteTextViewStyle
in the Activity
's theme. That is:
<style name="AppTheme" parent="...">
...
<item name="autoCompleteTextViewStyle">@style/LowThreshold.AutoCompleteTextView</item>
</style>
<style name="LowThreshold.AutoCompleteTextView" parent="Widget.AppCompat.AutoCompleteTextView">
<item name="android:completionThreshold">1</item>
</style>
Note that the autoCompleteTextViewStyle
attribute does not have the android
prefix, because the actual class in SearchView
is a subclass of AppCompatAutoCompleteTextView
, which has its own default style attribute.