目录
本文作者通过对AutoCompleteTextView使用,来实现对单词的模糊搜索。
介绍
AutoCompleteTextView是Android中的一个视图控件,它是EditText和ListView的组合。它提供了自动完成文本输入的功能,并显示与用户输入相匹配的建议列表。
AutoCompleteTextView的主要特性和用途如下:
-
自动完成功能:AutoCompleteTextView通过监视用户的文本输入,在用户输入时自动弹出与输入内容相匹配的建议列表。用户可以从列表中选择一个建议项,或继续输入以缩小建议列表。
-
建议列表:AutoCompleteTextView的下拉列表显示与用户输入相匹配的建议项。建议项通常是基于预定义的数据源(例如数组、数据库查询结果等)生成的,也可以通过自定义适配器来提供。
-
自定义适配器:可以使用自定义适配器来控制建议列表的显示方式和内容。通过扩展
ArrayAdapter
或CursorAdapter
等适配器类,可以自定义建议列表的外观和数据源。 -
异步数据加载:AutoCompleteTextView支持异步加载建议数据。可以在后台线程中执行数据的获取和处理,并在数据准备好后更新建议列表。
-
文本过滤:AutoCompleteTextView可以根据用户的输入进行过滤,只显示与输入内容匹配的建议项。过滤可以基于前缀匹配、全文匹配等方式进行。
-
事件监听:AutoCompleteTextView可以设置文本变化监听器(
TextWatcher
),以便在用户输入文本时执行特定的操作。可以根据用户的输入动态更新建议列表或执行其他相关操作。 -
自定义样式和布局:可以通过在布局文件中使用自定义的样式和布局属性,对AutoCompleteTextView进行外观上的自定义,以使其与应用程序的设计风格一致。
xml属性
android:completionHint:设置下拉菜单中的提示标题
android:completionHintView:定义提示视图中显示下拉菜单
android:completionThreshold:指定用户至少输入多少个字符才会显示提示
android:dropDownAnchor:设置下拉菜单的定位"锚点"组件,如果没有指定改属性, 将使用该TextView作为定位"锚点"组件
android:dropDownHeight:设置下拉菜单的高度
android:dropDownWidth:设置下拉菜单的宽度
android:dropDownHorizontalOffset:指定下拉菜单与文本之间的水平间距
android:dropDownVerticalOffset:指定下拉菜单与文本之间的竖直间距
android:dropDownSelector:设置下拉菜单点击效果
android:popupBackground:设置下拉菜单的背景
定义一个单词类
public class Vocabulary {
public String word;
public Vocabulary() {
}
编辑xml文件
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/autoComplete_word"
android:hint="查询单词"
android:completionThreshold="2"
android:dropDownHeight="wrap_content"
android:dropDownWidth="wrap_content"/>
编写适配器
模糊搜索结果的展示是一个ListView或者RecycleView,同时还需要根据自己需求来定义模糊搜索的规则,所以需要我们实现一个自己的适配器。这里我使用的是ListView,所以定义了ListView的适配器。
一个基础的适配器
定义一个WordAdapter类,代码如下:
public class WordAdapter extends ArrayAdapter<Vocabulary> {
private int layoutRes;
private List<Vocabulary> originalList = null;
private List<Vocabulary> filteredList;
public WordAdapter(@NonNull Context context, int resource, @NonNull List<Vocabulary> objects) {
super(context, resource, objects);
layoutRes = resource;
originalList = new ArrayList<>(objects);
filteredList = new ArrayList<>();
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Vocabulary vocabulary = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(layoutRes,parent,false);
viewHolder = new ViewHolder();
viewHolder.textView = (TextView) view.findViewById(R.id.word);
view.setTag(viewHolder);
}else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.textView.setText(vocabulary.word);
return view;
}
class ViewHolder{
TextView textView;
}
}
添加过滤器(用于根据自己的规则实现模糊搜索)
在WordAdapter内重写三个方法,代码如下:
@Override
public int getCount() {
return filteredList.size();
}
@Nullable
@Override
public Vocabulary getItem(int position) {
return filteredList.get(position);
}
@NonNull
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults();
filteredList.clear();
if (charSequence == null || charSequence.length() == 0){
}else {
String filterPattern = charSequence.toString().toLowerCase().trim();
for (Vocabulary item : originalList){
if (item.word.contains(filterPattern))
filteredList.add(item);
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
if (filterResults != null && filterResults.count > 0) {
clear();
addAll((List<Vocabulary>) filterResults.values);
notifyDataSetChanged();
}
// if (filterResults != null && filterResults.count > 0) {
// notifyDataSetChanged();
// } else {
// notifyDataSetInvalidated();
// }
}
};
}
if (item.word.contains(filterPattern))
filteredList.add(item);
这段代码就把可以集合中=item.word字段内含有filterPattern的所有元素过滤出来,当然,你也可以使用starWith过滤。
注意,一定要重写getItem()和getCount()方法,否则会出错。我也是因为这个问题,改了很久。
下面是WordAdapter的完整代码:
public class WordAdapter extends ArrayAdapter<Vocabulary> {
private int layoutRes;
private List<Vocabulary> originalList = null;
private List<Vocabulary> filteredList;
public WordAdapter(@NonNull Context context, int resource, @NonNull List<Vocabulary> objects) {
super(context, resource, objects);
layoutRes = resource;
originalList = new ArrayList<>(objects);
filteredList = new ArrayList<>();
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Vocabulary vocabulary = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(layoutRes,parent,false);
viewHolder = new ViewHolder();
viewHolder.textView = (TextView) view.findViewById(R.id.word);
view.setTag(viewHolder);
}else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.textView.setText(vocabulary.word);
return view;
}
class ViewHolder{
TextView textView;
}
@Override
public int getCount() {
return filteredList.size();
}
@Nullable
@Override
public Vocabulary getItem(int position) {
return filteredList.get(position);
}
@NonNull
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults();
filteredList.clear();
if (charSequence == null || charSequence.length() == 0){
}else {
String filterPattern = charSequence.toString().toLowerCase().trim();
for (Vocabulary item : originalList){
if (item.word.contains(filterPattern))
filteredList.add(item);
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
if (filterResults != null && filterResults.count > 0) {
clear();
addAll((List<Vocabulary>) filterResults.values);
notifyDataSetChanged();
}
}
};
}
}
在活动中使用(这里其实我是在碎片中使用的)
AutoCompleteTextView autoText = (AutoCompleteTextView) view.findViewById(R.id.autoComplete_word);
getWordList();//这是我的自定义方法,在数据库中获取单词集合
WordAdapter adapter = new WordAdapter(getContext(),R.layout.word_item,wordList);
autoText.setAdapter(adapter);
autoText.setOnItemClickListener(new AdapterView.OnItemClickListener() {//设置item的点击事件
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
ArrayAdapter<Vocabulary> arrayAdapter = (ArrayAdapter<Vocabulary>) adapterView.getAdapter();
Vocabulary vocabulary = arrayAdapter.getItem(i);
Intent intent = new Intent(getContext(),ShowWordActivity.class);
intent.putExtra("word",vocabulary.word);
startActivity(intent);
}
});
实现效果
这里我用的是contains()方法,你也可以采用startWith()方法过滤。