Android ListView用EditText实现搜索功能效果
作者:tonycheng93 发布时间:2021-05-29 09:05:25
前言
最近在开发一个IM项目的时候有一个需求就是,好友搜索功能。即在EditText中输入好友名字,ListView列表中动态展示刷选的好友列表。我把这个功能抽取出来了,先贴一下效果图:
分析
在查阅资料以后,发现其实Android中已经帮我们实现了这个功能,如果你的ListView使用的是系统的ArrayAdapter,那么恭喜你,下面的事情就很简单了,你只需要调用下面的代码就可以实现了:
searchEdittext.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user change the text
mAdapter.getFilter().filter(cs);
}
@Override
public void beforeTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
//
}
@Override
public void afterTextChanged(Editable arg0) {
//
}
});
你没看错,就一行 mAdapter.getFilter().filter(cs);便可以实现这个搜索功能。不过我相信大多数Adapter都是自定义的,基于这个需求,我去分析了下ArrayAdapter,发现它实现了Filterable接口,那么接下来的事情就比较简单了,就让我们自定的Adapter也去实现Filterable这个接口,不久可以实现这个需求了吗。下面贴出ArrayAdapter中显示过滤功能的关键代码:
public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
/**
* Contains the list of objects that represent the data of this ArrayAdapter.
* The content of this list is referred to as "the array" in the documentation.
*/
private List<T> mObjects;
/**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is also
* used by the filter (see {@link #getFilter()} to make a synchronized copy of
* the original array of data.
*/
private final Object mLock = new Object();
// A copy of the original mObjects array, initialized from and then used instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;
...
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
实现
首先写了一个Model(User)模拟数据
public class User {
private int avatarResId;
private String name;
public User(int avatarResId, String name) {
this.avatarResId = avatarResId;
this.name = name;
}
public int getAvatarResId() {
return avatarResId;
}
public void setAvatarResId(int avatarResId) {
this.avatarResId = avatarResId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自定义一个Adapter(UserAdapter)继承自BaseAdapter,实现了Filterable接口,Adapter一些常见的处理,我都去掉了,这里主要讲讲Filterable这个接口。
/**
* Contains the list of objects that represent the data of this Adapter.
* Adapter数据源
*/
private List<User> mDatas;
//过滤相关
/**
* This lock is also used by the filter
* (see {@link #getFilter()} to make a synchronized copy of
* the original array of data.
* 过滤器上的锁可以同步复制原始数据。
*
*/
private final Object mLock = new Object();
// A copy of the original mObjects array, initialized from and then used instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
//对象数组的备份,当调用ArrayFilter的时候初始化和使用。此时,对象数组只包含已经过滤的数据。
private ArrayList<User> mOriginalValues;
private ArrayFilter mFilter;
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
写一个ArrayFilter类继承自Filter类,我们需要两个方法:
//执行过滤的方法
protected FilterResults performFiltering(CharSequence prefix);
//得到过滤结果
protected void publishResults(CharSequence prefix, FilterResults results);
贴上完整的代码,注释已经写的不能再详细了
/**
* 过滤数据的类
*/
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
* <p/>
* 一个带有首字母约束的数组过滤器,每一项不是以该首字母开头的都会被移除该list。
*/
private class ArrayFilter extends Filter {
//执行刷选
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();//过滤的结果
//原始数据备份为空时,上锁,同步复制原始数据
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<>(mDatas);
}
}
//当首字母为空时
if (prefix == null || prefix.length() == 0) {
ArrayList<User> list;
synchronized (mLock) {//同步复制一个原始备份数据
list = new ArrayList<>(mOriginalValues);
}
results.values = list;
results.count = list.size();//此时返回的results就是原始的数据,不进行过滤
} else {
String prefixString = prefix.toString().toLowerCase();//转化为小写
ArrayList<User> values;
synchronized (mLock) {//同步复制一个原始备份数据
values = new ArrayList<>(mOriginalValues);
}
final int count = values.size();
final ArrayList<User> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final User value = values.get(i);//从List<User>中拿到User对象
// final String valueText = value.toString().toLowerCase();
final String valueText = value.getName().toString().toLowerCase();//User对象的name属性作为过滤的参数
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString) || valueText.indexOf(prefixString.toString()) != -1) {//第一个字符是否匹配
newValues.add(value);//将这个item加入到数组对象中
} else {//处理首字符是空格
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {//一旦找到匹配的就break,跳出for循环
newValues.add(value);
break;
}
}
}
}
results.values = newValues;//此时的results就是过滤后的List<User>数组
results.count = newValues.size();
}
return results;
}
//刷选结果
@Override
protected void publishResults(CharSequence prefix, FilterResults results) {
//noinspection unchecked
mDatas = (List<User>) results.values;//此时,Adapter数据源就是过滤后的Results
if (results.count > 0) {
notifyDataSetChanged();//这个相当于从mDatas中删除了一些数据,只是数据的变化,故使用notifyDataSetChanged()
} else {
/**
* 数据容器变化 ----> notifyDataSetInValidated
容器中的数据变化 ----> notifyDataSetChanged
*/
notifyDataSetInvalidated();//当results.count<=0时,此时数据源就是重新new出来的,说明原始的数据源已经失效了
}
}
}
特别说明
//User对象的name属性作为过滤的参数
final String valueText = value.getName().toString().toLowerCase();
这个地方是,你要进行搜索的关键字,比如我这里使用的是User对象的Name属性,就是把用户名当作关键字来进行过滤筛选的。这里要根据你自己的具体逻辑来进行设置。
if (valueText.startsWith(prefixString) || valueText.indexOf(prefixString.toString()) != -1)
在这里进行关键字匹配,如果你只想使用第一个字符匹配,那么你只需要使用这行代码就可以了:
//首字符匹配
valueText.startsWith(prefixString)
如果你的需求是只要输入的字符出现在ListView列表中,那么该item就要显示出来,那么你就需要这行代码了:
//你输入的关键字包含在了某个item中,位置不做考虑,即可以不是第一个字符
valueText.indexOf(prefixString.toString()) != -1
这样就完成了一个EditText + ListView实现搜索的功能。我在demo中用两种方法实现了这一效果。第一种是系统的ArrayAdapter实现,第二种是自定义Adapter实现。
demo下载地址:EditSearch_jb51.rar
来源:http://www.cnblogs.com/tonycheng93/p/5435845.html


猜你喜欢
- JAVA操作XML文档主要有四种方式,分别是DOM、SAX、JDOM和DOM4J,DOM和SAX是官方提供的,而JDOM和DOM4J则是引用
- 一、多线程的优缺点多线程的优点:1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快多线程的代价:1)设计更复杂虽然有一些多线程
- 一. 线程池简介1. 线程池的概念:线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建
- 一、概述;从字面上讲,就是停止这个世界,看到这个字眼,就觉得这是可怕的事情,那到底什么是stop-the-world?stop-the-wo
- 泛型中 T 类型变量 和 ? 通配符 区别定义不同 :T 是类型变量,? 是通配符使用范围不同:? 通配符用作 参数类型、字段类型、局部变量
- 将Program类的删除,将里面的静态类Main修改后放入服务类中:partial class Server1 : ServiceBase&
- 前言重试,我相信大家并不陌生。在我们调用Http接口的时候,总会因为某种原因调用失败,这个时候我们可以通过重试的方式,来重新请求接口。生活中
- 一、下载Xxl-Job源代码并导入本地并运行Github地址:https://github.com/xuxueli/xxl-job中文文档地
- 目录LinkedHashMap 实现继承 LinkedHashMap组合 LinkedHashMap链表 + HashMap 实现LRU,即
- 一、基于WINFORM下的选择对话框在WINFORM下,我们可以利用系统的对话框(MessageBox)来实现,具体思路是读取Message
- 本文实例为大家分享了Android实现五子棋小游戏的具体代码,供大家参考,具体内容如下配图:代码:package com.example.f
- 一、简介二、代码/activityLifeCycle_3Screen/AndroidManifest.xml<manifest xml
- SpringBoot默认加载的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中
- 一、什么是Activity?简单的说:Activity就是布满整个窗口或者悬浮于其他窗口上的交互界面。在一个应用程序中通常由多个Activi
- 文章开始之前,先看一下效果图,看是不是您正所需要的:一、构建计算器的界面要构建出一个好看点的计算器界面,还是需要颇费些小心思的,我做这个的时
- 本人是用易语言起步的,起初是为了兴趣,后来由于易语言被杀软误杀严重,连空白程序都杀,后来转到了学C#,随着学习的深入,接触越来越复杂的东西之
- Mybatis简介MyBatis的前身叫iBatis,本是apache的一个开源项目, 2010年这个项目由apache software
- 问题背景通常我们开发的时候,需要联合控制台和Navicat/PLSQL等工具进行语句的拼接检查,如果只是输出了一堆???,那么将极大降低我们
- 前言在上篇文章讲到了如何配置单数据源,但是在实际场景中,会有需要配置多个数据源的场景,比如说,我们在支付系统中,单笔操作(包含查询、插入、新
- 很多app中在第一次安装登陆时会有引导欢迎界面,第二次打开时就不再显示引导页面。这个功能可以通过使用SharePreferences将用户的