Android开发:SearchView与搜索
2025-05-03 19:05:45 拉莫斯世界杯
搜索控件SearchView介绍
我们在Android开发中,需要实现搜索功能,例如文章搜索、城市地点搜索和字典搜索等等,对此实现搜索功能可以使用SearchView,它是Android的一个控件,继承自LinearLayout。
SearchView的继承关系
类似于EditText,其能接收用户在搜索框输入的查询,通过适当配置可以将查询交给对应搜索Activity并返回相应结果,效果如下:
城市地点搜索
一般情况下就是SearchView和ListView(或是RecyclerView)配合使用,前者接收用户的查询,后者用于返回显示的查询结果。
SearchView的XML属性
SearchView具备xml属性无非就这五种,在设计布局时可以自己选择相应的属性进行设置。
属性关联方法描述 android:iconifiedByDefault
setIconifiedByDefault(boolean)搜索视图的默认状态(是否在搜索框内) android:imeOptions
setImeOptions(int) 要在查询文本字段上设置的 IME 选项。
必须是以下常量值的一个或多个(用“|”分隔)
android:inputType
setInputType(int) 要在查询文本字段上设置的输入类型。
必须是以下常量值的一个或多个(用“|”分隔)
android:maxWidth
setMaxWidth(int)设置SearchView最大宽度 android:queryHint
setQueryHint(CharSequence) 要在空查询字段中显示的可选查询提示字符串。
可以是字符串值,使用 '\\;' 为unicode字符转义诸如 '\\n' 或 '\\uxxxx' 之类的字符
若是使用SearchView进行搜索,则通常情况下将inputType属性设置成 "textFilter" 进行文本过滤,属性queryHint中输入想提示用户的内容,代码如下:
android:id="@+id/searchView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:iconifiedByDefault="false" android:inputType="textFilter" app:defaultQueryHint="请输入搜索内容" /> 为了适配其他Android版本机型,需要使用命名空间app,在布局中引入 xmlns:app="http://schemas.android.com/apk/res-auto" 这段代码。 实现可搜索的Activity 只有SearchView的话可不能实现搜索功能,在用户眼里就和EditView无异。这时就要进入激动人心的java代码书写,其中步骤如下: 绑定SearchView和ListView并设置二者的属性实现ListView的适配器Adapter和其中的过滤规则在Activity中重写搜索改变方法设置ListView各项的监听器实现后续逻辑 其中第一步过于简单可以略过,从第二步开始其实就是ListView的相关内容,即数据List和Adapter适配 适配器Adatper和过滤规则Filter 关于Adatper的内容不多赘述,其中值得注意的是,在写ListView的Adapter时不光继承BaseAdapter,同时需要实现Filterable,即 public class SearchAdapter extends BaseAdapter implements Filterable {...} 来创建我们的过滤规则,而且对于绑定的数据,List需要两份,一份用于数据和ListView的绑定,另一份用于展示过滤后的数据,演示的java代码如下: public class SearchAdapter extends BaseAdapter implements Filterable { private Context context; private List private List public SearchAdapter(Context context, List this.context = context; this.SearchList = list; this.backSearchList = list; } //... } 接着便要着手我们的自定义过滤规则了,通过继承Filter类,重写performFiltering方法定义规则如下: /** * 定义过滤器的类来定义过滤规则 */ class MyFilter extends Filter { //在performFiltering(CharSequence charSequence)这个方法中定义过滤规则 @Override protected FilterResults performFiltering(CharSequence constraint) { List if (!TextUtils.isEmpty(constraint)) { //当过滤的关键字不为空的时候,把符合条件的数据对象添加到集合中 for (String search : searchList) { if (search.contains(constraint)) { //要匹配的item中的view list.add(search); } } } FilterResults results = new FilterResults(); results.values = list; results.count = list.size(); return results; } 其中我们先定义了一个list来暂存过滤后的结果数据列表,通过判断用户输入的constraint内容后,如果不是空则遍历原先定义的search,并把包含constraint的内容保存到list中,最后返回包含list数据和大小的result过滤结果。 然后重写publishResults方法让适配器更新搜索界面。 //在publishResults方法中让适配器更新界面 @Override protected void publishResults(CharSequence constraint, FilterResults results) { backSearchList = (List if (results.count > 0) { notifyDataSetChanged(); //通知数据发生了改变 } else { notifyDataSetInvalidated(); //通知数据失效 } } 在代码中我们将先前返回的results的内容保存至另一份backSearchList中,并通知数据改变与否。 然后别忘了在SearchAdapter类中声明我们的自定义myFilter,同时需要重写getFilter方法。 下面是完整的SearchAdatper类的代码: public class SearchAdapter extends BaseAdapter implements Filterable { private Context context; private List private List MyFilter mFilter; //Search为自定义的类 public SearchAdapter(Context context, List this.context = context; this.searchList = searchList; this.backSearchList = searchList; } public SearchAdapter() { } //getCount和getItem都要返回backSearchList的数据 @Override public int getCount() { return backSearchList.size(); } @Override public Object getItem(int position) { return backSearchList.get(position); } @Override public long getItemId(int position) { return position; } class ViewHolder { //定义控件 } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = View.inflate(context, R.layout.activity_search_item, null); holder = new ViewHolder(); //holder定义的控件绑定布局控件 convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //控件内容和list数据绑定 return convertView; } @Override public Filter getFilter() { if (mFilter == null) { mFilter = new MyFilter(); } return mFilter; } /** * 定义过滤器的类来定义过滤规则 */ class MyFilter extends Filter { //在performFiltering(CharSequence charSequence)这个方法中定义过滤规则 @Override protected FilterResults performFiltering(CharSequence constraint) { List if (!TextUtils.isEmpty(constraint)) { //当过滤的关键字不为空的时候,把符合条件的数据对象添加到集合中 for (Search search : citySearchList) { if (search.getAreaName().contains(constraint)) { //要匹配的item中的view list.add(search); } } } //results保存过滤的数据 FilterResults results = new FilterResults(); results.values = list; results.count = list.size(); return results; } //在publishResults方法中让适配器更新界面 @Override protected void publishResults(CharSequence constraint, FilterResults results) { backSearchList = (List if (results.count > 0) { notifyDataSetChanged(); //通知数据发生了改变 } else { notifyDataSetInvalidated(); //通知数据失效 } } } } 需要注意的是,在重写getCount和getItem要返回的是backSearchList的数据,因为最后为用户展示的是过滤后的结果,这些结果是与backSearchList数据对应的。 在活动Activity中重写搜索方法 上面一口气说了这么多,接下来就简单许多了,在我们的搜索Activity中只需重写onQueryTextChange和onQueryTextSubmit两个方法,前者在用户输入字符时激发,后者则是单击搜索按钮时激发。 @Override public boolean onQueryTextChange(String newText) { //如果newText是长度为0的字符串 if (TextUtils.isEmpty(newText)) { //清除ListView的过滤 searchListView.clearTextFilter(); searchAdapter.getFilter().filter(""); } else { //使用用户输入的内容对ListView的列表项进行过滤 //searchListView.setFilterText(newText); searchAdapter.getFilter().filter(newText); } return true; } @Override public boolean onQueryTextSubmit(String query) { return true; } 在其中我们不难发现,当搜索框中没有输入时,先使用clearTextFilter清除原本的过滤,同时设置filter为空,这样不显示没有过滤的全部数据,当然如果想显示所有数据那就删除这句代码。如果搜索框有内容时就将newText设置过滤,此时ListView就会显示包含newText搜索内容的数据。然后在Activity中添加 searchView.setOnQueryTextListener(this); 这句代码以设置SearchView的监听器 完成你的后续逻辑 你觉得仅是这样就结束了吗,这个时候只是完成了搜索和过滤功能,用户只能进行搜索,如果用户想搜索后点击查看,例如百度搜索跳转界面。这个时候你还需要添加你自己的后续逻辑,如ListView中设置监听器。 searchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { //search获取点击的item Search search = (Search) parent.getAdapter().getItem(position); //后续逻辑例如根据search内容跳转 } }); 中间的内容就根据自己的代码进行修改,例如使用intent跳转。 后记 这篇文章是在本人开发天气App所遇到的技术问题中得到的经验和总结,若是文章中出现的错误还望批评指正,另附上参考链接和天气App的GitHub地址: 创建搜索界面 | Android 开发者 | Android Developers (google.cn) SearchView | Android Developers (google.cn) Gilbert2510/WindowWeather (github.com)