在 ArrayAdapter 中使用过滤器时,SearchView 仅第二次触发

SearchView fires only the second time when using a filter in ArrayAdapter

我在工具栏中添加了一个 SearchView 来过滤一些项目。

@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);
}

在我的 MainActivity 中,我得到一个项目列表并将其传递给我的适配器:

ItemsArrayAdapter adapter = new ItemsArrayAdapter(this, itemList);
autoComplete.setAdapter(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;
        }
    };
}

列表中存在的项目数是 26。例如,当我从单词 car 写一封信 c 时,returned 的项目数是 26 ,即使包含 c 的单词只有 11 个。当我键入第二个字母 a 时, returned 的项目数是 1(这是正确的)并且该项目是正确显示在视图中。如何使 SearchView 第一次触发并 return 正确的项目数?谢谢。

SearchView 中的 search_src_textAutoCompleteTextView 的后代,它继承了 class 的大部分行为。 AutoCompleteTextView 的默认阈值为 2 个字符。也就是说,在您输入至少 2 个字符之前它不会开始过滤,这解释了为什么您只输入一个字符时会看到完整的列表。我们可以通过将阈值降低到 1 来解决这个问题。

AutoCompleteTextViewsetThreshold() 方法,所以我们可以简单地用参数 1 调用它。但是,正如你所指出的,这会导致 can only be called from within the same library group lint 警告,因为androidx SearchView 使用的特定 subclass 在源代码中标记为 @hide。这可以很容易地用 @SuppressLint("RestrictedApi") 来抑制,但是如果你不想使用受限的 API,还有另一种方法。

我们可以通过在默认 AutoCompleteTextView 样式的子样式中指定的 android:completionThreshold XML 属性来设置该阈值,然后我们将其设置为 autoCompleteTextViewStyle Activity 的主题。即:

<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>

注意autoCompleteTextViewStyle属性没有android前缀,因为SearchView中的实际class是[=的子class 28=],它有自己的默认样式属性。