写给自己和各位读者:
首先说本人目前还是个菜鸟,每当遇到问题时,第一时间想到的是在网上查找大神的解决方案,其实在借鉴他人的方法时,也是在不断地提升自己,不管是代码的阅读能力,还是接收新知识,还是仅仅为以后的项目开发留下一个印象,都起到了很好的帮助。总之,多上网查找资料,多看一些好的解决方法,当你在实际开发中遇到这些问题时你就可以信手拈来。下面是我最近碰到的,通过在文本框中输入内容,如何实时刷新ListView的问题。
下面是效果图:
搜索前:
搜索后:
根据使用的ListView适配器的不同,有两种不同的实现方法。
第一种,如果你的ListView使用的是系统的ArrayAdapter,那么这个事情就变得很简单,只需要几行代码就可以解决:
//给编辑框添加监听事件
etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//文本改变时,给adapter设置过滤器就行了,只需要一行代码
listViewAdapter.getFilter().filter(charSequence);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
第二种:由于大多数Adapter都是自定义的,所以不能直接调用上面这个方法,但是我们可以模仿ArrayAdapter来实现这个功能。那就是实现Filterable接口,通过实现Filterrable接口,我们就可以像上面那样调用getFilter()和filter()方法了。
首先要有一个实体类,用来模拟数据。
public class AppInfoBitmap {
private String mName;
private Bitmap mBitmap;
//无参构造
public AppInfoBitmap() {
}
//带参构造
public AppInfoBitmap(String mName, Bitmap mBitmap) {
this.mName = mName;
this.mBitmap = mBitmap;
}
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public Bitmap getmBitmap() {
return mBitmap;
}
public void setmBitmap(Bitmap mBitmap) {
this.mBitmap = mBitmap;
}
}
接下来自定义一个Adapter(ListViewAdapter)继承自BaseAdapter,实现了Filterable接口。
实现Filterable,必须要实现两个方法。
分别是:
protected FilterResults performFiltering(CharSequence charSequence);
protected void publishResults(CharSequence charSequence, FilterResults filterResults);
其次我们向外提供了一个getFilter()方法,用来生成Filter对象,进而调用Filter类中的filter()方法,最终实现过滤功能。
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
下面就是比较完整的代码了(此处没有包含Adapter一些常用处理,用最简洁的代码来说明问题):
public class AppListViewAdapter extends BaseAdapter implements Filterable{
private Context mContext;
//Adapter数据源
private List<AppInfoBitmap> mAppInfoList;
//过滤相关
/*
* 过滤器上的锁可以同步复制原始数据
* */
private final Object mLock=new Object();
//对象数组的备份,当调用ArrayFilter的时候初始化和使用。此时,对象数组只包含已经过滤的数据。
private ArrayList<AppInfoBitmap> mOriginalValues;
private ArrayFilter mFilter;
@Override
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
//写一个ArrayFilter类继承Filter
public class ArrayFilter extends Filter{
//执行过滤的方法
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
/*
* 一个带有首字母约束的数组过滤器,每一项
* 不是以该首字母开头的都会被移除该list
* */
//过滤的结果
FilterResults results=new FilterResults();
//原始数据为空时,上锁,同步复制原始数据
if (mOriginalValues==null){
synchronized (mLock){
mOriginalValues=new ArrayList<>(mAppInfoList);
}
}
//当首字母为空时
if (charSequence==null||charSequence.length()==0){
ArrayList<AppInfoBitmap> list;
synchronized (mLock){
//同步复制一个原始备份数据
list=new ArrayList<>(mOriginalValues);
}
//此时返回的results就是原始的数据,不必进行过滤
results.values=list;
results.count=list.size();
}else {
//转化为小写
String prefixString=charSequence.toString().toLowerCase() ;
ArrayList<AppInfoBitmap> values;
//同步复制一个原始备份数据
synchronized (mLock){
values=new ArrayList<>(mOriginalValues);
}
final int count=values.size();
final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();
for (int i = 0; i < count; i++) {
//从ArrayList<AppInfoBitmap>中拿到AppInfoBitmap对象
final AppInfoBitmap value=values.get(i);
/*
* 这个地方是,要进行搜索的关键字,将app的name属性当作关键字来进行筛选
* */
final String valueText=value.getmName().toString().toLowerCase();
/*
* 首字符匹配:valueText.startsWith(prefixString)
* 输入的关键字包含在了某个item中:valueText.indexOf(prefixString.toString())!=-1
* */
if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
newValues.add(value);
}
else {
//处理首字符是空格,去掉text中的空格,然后赋值给字符串words
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就是过滤后的List<AppInfoBitmap>
results.values=newValues;
results.count=newValues.size();
}
//将结果返回
return results;
}
//筛选结果
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
//此时,Adapter数据源就是过滤后的esults
mAppInfoList=(List<AppInfoBitmap>) filterResults.values;
/*
* 数据容器变化------->notifyDataSetInValidated
* 容器中数据发生变化------->notifyDataSetChanged
* */
if (filterResults.count>0){
//这个相当于从mAppInfoList中删除了一些数据,只是数据的变化,所以使用notifyDataSetChanged()
notifyDataSetChanged();
}else {
//当results.count<=0,此时数据源就是重新new出来的,说明原始数据源已经失效了
notifyDataSetInvalidated();
}
}
}
}
下面是我的完整的Aadpter代码,希望可以帮助到陷入沉思的你。
public class AppListViewAdapter extends BaseAdapter implements Filterable{
private Context mContext;
//Adapter数据源
private List<AppInfoBitmap> mAppInfoList;
//过滤相关
/*
* 过滤器上的锁可以同步复制原始数据
* */
private final Object mLock=new Object();
//对象数组的备份,当调用ArrayFilter的时候初始化和使用。此时,对象数组只包含已经过滤的数据。
private ArrayList<AppInfoBitmap> mOriginalValues;
private ArrayFilter mFilter;
//带参构造
public AppListViewAdapter(Context context, List<AppInfoBitmap> appInfoList) {
this.mContext = context;
this.mAppInfoList = appInfoList;
}
@Override
public int getCount() {
return mAppInfoList.size();
}
@Override
public AppInfoBitmap getItem(int i) {
return mAppInfoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
//通过ViewHolder优化listView
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder=new ViewHolder();
if (view==null){
view=View.inflate(mContext, R.layout.activity_app_list_item,null);
//获取布局控件
viewHolder.imageView=view.findViewById(R.id.app_image);
viewHolder.textView=view.findViewById(R.id.app_name);
//设置布局控件
viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
viewHolder.textView.setText(mAppInfoList.get(i).getmName());
//设置Tag
view.setTag(viewHolder);
}else {
//获取Tag
viewHolder=(ViewHolder) view.getTag();
//设置布局控件
viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
viewHolder.textView.setText(mAppInfoList.get(i).getmName());
}
//将view视图返回
return view;
}
public class ViewHolder{
ImageView imageView;
TextView textView;
}
@Override
public Filter getFilter() {
if (mFilter==null){
mFilter=new ArrayFilter();
}
return mFilter;
}
//写一个ArrayFilter类继承Filter
public class ArrayFilter extends Filter{
//执行过滤的方法
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
/*
* 一个带有首字母约束的数组过滤器,每一项
* 不是以该首字母开头的都会被移除该list
* */
//过滤的结果
FilterResults results=new FilterResults();
//原始数据为空时,上锁,同步复制原始数据
if (mOriginalValues==null){
synchronized (mLock){
mOriginalValues=new ArrayList<>(mAppInfoList);
}
}
//当首字母为空时
if (charSequence==null||charSequence.length()==0){
ArrayList<AppInfoBitmap> list;
synchronized (mLock){
//同步复制一个原始备份数据
list=new ArrayList<>(mOriginalValues);
}
//此时返回的results就是原始的数据,不必进行过滤
results.values=list;
results.count=list.size();
}else {
//转化为小写
String prefixString=charSequence.toString().toLowerCase() ;
ArrayList<AppInfoBitmap> values;
//同步复制一个原始备份数据
synchronized (mLock){
values=new ArrayList<>(mOriginalValues);
}
final int count=values.size();
final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();
for (int i = 0; i < count; i++) {
//从ArrayList<AppInfoBitmap>中拿到AppInfoBitmap对象
final AppInfoBitmap value=values.get(i);
/*
* 这个地方是,要进行搜索的关键字,将app的name属性当作关键字来进行筛选
* */
final String valueText=value.getmName().toString().toLowerCase();
/*
* 首字符匹配:valueText.startsWith(prefixString)
* 输入的关键字包含在了某个item中,位置不做考虑
* */
if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
newValues.add(value);
}
else {
//处理首字符是空格,去掉text中的空格,然后赋值给字符串words
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就是过滤后的List<AppInfoBitmap>
results.values=newValues;
results.count=newValues.size();
}
//将结果返回
return results;
}
//筛选结果
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
//此时,Adapter数据源就是过滤后的esults
mAppInfoList=(List<AppInfoBitmap>) filterResults.values;
/*
* 数据容器变化------->notifyDataSetInValidated
* 容器中数据发生变化------->notifyDataSetChanged
* */
if (filterResults.count>0){
//这个相当于从mAppInfoList中删除了一些数据,只是数据的变化,所以使用notifyDataSetChanged()
notifyDataSetChanged();
}else {
//当results.count<=0,此时数据源就是重新new出来的,说明原始数据源已经失效了
notifyDataSetInvalidated();
}
}
}
}