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

private List backSearchList;

public SearchAdapter(Context context, List 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 list = new ArrayList<>();

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) results.values;

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

private List backSearchList;

MyFilter mFilter;

//Search为自定义的类

public SearchAdapter(Context context, List searchList) {

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 list = new ArrayList<>();

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) results.values;

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)