在 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_text
是 AutoCompleteTextView
的后代,它继承了 class 的大部分行为。 AutoCompleteTextView
的默认阈值为 2 个字符。也就是说,在您输入至少 2 个字符之前它不会开始过滤,这解释了为什么您只输入一个字符时会看到完整的列表。我们可以通过将阈值降低到 1 来解决这个问题。
AutoCompleteTextView
有 setThreshold()
方法,所以我们可以简单地用参数 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=],它有自己的默认样式属性。
我在工具栏中添加了一个 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_text
是 AutoCompleteTextView
的后代,它继承了 class 的大部分行为。 AutoCompleteTextView
的默认阈值为 2 个字符。也就是说,在您输入至少 2 个字符之前它不会开始过滤,这解释了为什么您只输入一个字符时会看到完整的列表。我们可以通过将阈值降低到 1 来解决这个问题。
AutoCompleteTextView
有 setThreshold()
方法,所以我们可以简单地用参数 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=],它有自己的默认样式属性。