1

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.

Jorn
  • 252
  • 1
  • 15
  • Where are you calling the `Filter`? – Mike M. Sep 11 '19 at 20:40
  • @MikeM. I'm only adding `ItemsArrayAdapter adapter = new ItemsArrayAdapter(this, itemList);` and `autoComplete.setAdapter(adapter);` in my `MainActivity`. I'm not calling any filter. – Jorn Sep 12 '19 at 15:22
  • Oh, I see what you're doing, now. Call `setThreshold(1)` on `autoComplete`. – Mike M. Sep 13 '19 at 02:17
  • @MikeM. Are you kidding me? Beside the fact that I get `SearchAutocomplete.seTreshold can only be called from within the same library group` and I use `@SuppressLint("RestrictedApi")` to supress it, IT'S WORKING. I can't belive that. Thank you so much. Please add it as an answer so other can see it. I have struggled with this problem for two days. – Jorn Sep 13 '19 at 09:12
  • 1
    Cool. Lemme see if we can do it without the restricted API call, first, though. I'll have a look around the source again later, when I get some time. Glad we got something working, anyway. Cheers! – Mike M. Sep 13 '19 at 10:23
  • 1
    @MikeM. It would be great if I can use that without the restricted API call. Thanks again. – Jorn Sep 13 '19 at 10:35
  • 1
    OK, I _think_ we can do this without that restricted call, but it'll have to be through a style: https://drive.google.com/file/d/11t8qB357t4OIm6Dm9RPuqvvKplRA1lAA/view?usp=drivesdk. I don't have an androidx setup available, atm, so just give that a test, at your leisure. If it works for ya, I'll get an answer written up, but it might not be until late tonight. – Mike M. Sep 13 '19 at 11:26
  • I got the idea by setting it in a style, and yes it worked again. Looking forward for your answer. – Jorn Sep 13 '19 at 12:02

1 Answers1

1

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.

Mike M.
  • 38,532
  • 8
  • 99
  • 95