Android ListView快速定位(二)

方法二:android:textFilterEnabled="true" + Filter

这个属性在android.widget.AbsListView下,要求adapter必须实现Filterable接口
索引.png
参考: http://justcallmebrian.com/?p=149

实现步骤:

1.listview
布局文件中设置android:textFilterEnabled="true"
或代码中listview.setTextFilterEnabled(true);

2.adapter实现Filterable接口
Filterable接口很简单,就只有一个方法
索引.png
这里要求返回一个Filter对象。
Filter是一个抽象类,需要自己写个类MyFilter,重写其中的方法
索引.png
performFiltering方法,将用户输入的条件传过来,在这个方法中可以按照自己需要的条件过滤
publishResults中可以将过滤后的新数据传给adapter(通过一个全局变量 或者自己定义一个变量,提供get方法)

重写了上述两个方法后,外部可以通过adapter.getFilter().filter(s)完成过滤
索引.png
另外,Filter还有一个内部类和一个内部接口
索引.png
Filter.FilterListener中有一个onFilterComplete方法
索引.png
Filter.FilterResults中有两个成员变量,用于存储过滤时的值
索引.png

例: 实现MyFilter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private class MyFilter extends Filter{

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {

        //存储过滤的值
        FilterResults retval = new FilterResults(); 
        retval.values = list_data;
        retval.count = list_data.size();

        //有筛选条件时,显示被筛选的内容; 没有筛选条件时,显示所有内容
        if(constraint != null && constraint.toString().length() > 0) {

            constraint = constraint.toString().toUpperCase();
            List<Map<String, String>> filt = new ArrayList<Map<String, String>>();
            List<Map<String, String>> tmpItems = new ArrayList<Map<String, String>>();
            tmpItems.addAll(origin_items);
            for(int i = 0; i < tmpItems.size(); i++) {

                Map<String, String> sf = tmpItems.get(i);
                if( sf.get("cid").toUpperCase().contains(constraint)
                || sf.get("type").toUpperCase().contains(constraint)
                || sf.get("title").toUpperCase().contains(constraint)
                || sf.get("answer").toUpperCase().contains(constraint)) {

                    filt.add(sf);

                }

            }
            retval.count = filt.size();
            retval.values = filt;

        }
        return retval;

    }


    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {

        //先清空旧数据
        filteredItems.clear();
        adapter.notifyDataSetChanged();

        //再添加新数据
        filteredItems.addAll((List<Map<String, String>>)results.values);
        adapter.notifyDataSetChanged();
        }

    }

}

例: 实现Filterable接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyAdapter extends BaseAdapter implements Filterable{
               private MyFilter filter;
                private List<Map<String, String>> filteredItems = new ArrayList<Map<String, String>>();

                public View getView(int position, View convertView, ViewGroup parent) {
                      //操作filteredItems 
                }

                @Override

        public Filter getFilter() {

            if(filter == null){

                filter = new MyFilter();

            }
            return filter;

        }

}

注意: 这里我犯了一个比较严重的错误

关于过滤后的数据filteredItems的存储,我原来是直接

1
 filteredItems = (List<Map<String, String>>)results.values;

adapter中的绑定数据的方法也是直接用"="

1
2
3
4
5
6
  public void setData(List<Map<String, String>> list){

        this.filteredItems = list;
        this.notifyDataSetChanged();

        }

因为我希望筛选的时候,如果条件为空,还是要把原来所有的数据都显示出来。
然而使用" = " 这个写法其实是将引用指给了我最原始的数据,前后虽然有2个变量,但是指向的是同一个空间。
那么,当我publishResults时,会先清空旧数据,一旦清空,我的数据就会被我清掉,再也找不回来。

解决办法:
后来,我声明时直接给它实例化、分配了新空间。
使用时统一使用addAll方法,这样就相当于把数据复制一份出来,对复制的数据进行清空。
那么我每一次筛选条件为空时,还是可以取到原始的所有数据,显示出来。

效果图:
图片标题

原文地址:https://www.cnblogs.com/Free-Thinker/p/3632520.html