AutoCompleteTextView,实现自定义规则的自动补全功能
2016-11-13 00:00
495 查看
某个项目需要做关键词筛选自动补全的功能,并且需要对文本中间的文字也能过滤,当然做这个功能首选的控件当然是AutoCompleteTextView,正常情况该控件只能对前面的字符进行匹配,如果要文本中间的文字也进行匹配就需要自己实现自定义规则了,大概搜了一下,自定义规则网上已经有了,但是相对完整的资料不多,并且大部分都是直接用ArrayAdapter配合String类型来使用,但这样使用相对来说,实用性不强,因为开发中,往往需要过滤的是class中某一个字段的数据,例如下面的实体类:
需求分析:我们需要对上面实例的name属性进行筛选过滤,如果是用ArrayAdapter,那么我们就需要用一个集合去遍历保存所有PersonInfo的name数据,然后再填充到ArrayAdapter,这样处理起来很麻烦,并且扩展性不强,如果客户要求筛选的下拉列表,要同时显示姓名和手机号,那么此时直接使用ArrayAdapter就不再满足需求了。那么最简单的办法当然是继承BaseAdapter然后自定义一个adapter了,这个adapter和我们平常自定义ListView的adapter有一点区别,因为要实现自定义过滤规则,所以必须实现Filterable接口。
泛型
抽象
自定义Filterable实现数据过滤规则
![](https://static.oschina.net/uploads/img/201611/14093431_7yz5.gif)
说明:
这里用到了抽象和泛型,主要是为了提高代码的复用性(T 和 abstract),如果不了解或者忘记了,就google吧。。。。
2.增强复用型的adapter,国际惯例,先看代码
说明:
Filterable接口: adapter必须实现该接口后再配合AutoCompleteTextView用,否则是无法筛选填充数据的
ListCallback<T>接口:该接口中的方法对应List集合部分方法实现,因为想到要提高复用性,所以直接adapter内部添加了一个List集合,之后使用该adapter就不用再添加List集合了,直接调用adapter.add(data),adapter.addAll(listData)等方法即可添加、修改数据,这样相对来说可以减少adapter使用时的代码数量,提高复用;
onFilterRule方法:抽象方法,让子类去实现自己的过滤规则,因为实际开发中,每个adapter的数据和过滤规则,不一定一样。
onBindDataToView方法:。用于绑定listView item数据
3.BaseFilterAdapter的用法
说明:
有没有发现,此时的adapter简洁了很多,比一般adapter的写法少了很多代码,哈哈哈
因为我们是需要对PersonInfo的名字进行数据过滤填充,所以泛型直接传PersonInfo
onFilterRule方法,在该方法中实现了自己的过滤规则,这里用的是name字段,当然同时也可以添加多个判断条件
onBindDataToView方法,绑定数据到view
4.具体的使用代码,列出几个主要的方法
四、总结:这样设计代码,结构清晰,简单明了。同时又提高了复用性,以后如果有类似的需求,写adapter时直接继承BaseFilterAdapter,实现自己的过滤逻辑即可,无须再重新写一遍处理代码和逻辑,可以省下很大的工作量。
实例代码:https://github.com/wangzhiyuan888/SampleExample/tree/master
public class PersonInfo { private String name; private String phone; }
需求分析:我们需要对上面实例的name属性进行筛选过滤,如果是用ArrayAdapter,那么我们就需要用一个集合去遍历保存所有PersonInfo的name数据,然后再填充到ArrayAdapter,这样处理起来很麻烦,并且扩展性不强,如果客户要求筛选的下拉列表,要同时显示姓名和手机号,那么此时直接使用ArrayAdapter就不再满足需求了。那么最简单的办法当然是继承BaseAdapter然后自定义一个adapter了,这个adapter和我们平常自定义ListView的adapter有一点区别,因为要实现自定义过滤规则,所以必须实现Filterable接口。
一、涉及到的知识点
自定义Adapter泛型
抽象
自定义Filterable实现数据过滤规则
二、最终效果图
![](https://static.oschina.net/uploads/img/201611/14093431_7yz5.gif)
三、实现代码
1.先看看自定义Filterable的代码,CustomFilterRule类public abstract class CustomFilterRule<T> extends Filter { private List<T> mUnfilteredData; public CustomFilterRule(List<T> data) { this.mUnfilteredData = new ArrayList<>(data); } @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); String prefixString = constraint.toString().toLowerCase(); ArrayList<T> newValues = (ArrayList<T>) onFilterData(prefixString, mUnfilteredData); results.values = newValues; results.count = newValues.size(); return results; } /** * 如果存在动态添加过滤数据,重新调用该方法,set数据即可 */ public void setmUnfilteredData(List<T> data) { this.mUnfilteredData = new ArrayList<>(data); } /** * 因为筛选规则不是完全确定的,所以公开一个抽象方法,让子类去实现 */ public abstract List<T> onFilterData(String prefixString, List<T> unfilteredValues); }
说明:
这里用到了抽象和泛型,主要是为了提高代码的复用性(T 和 abstract),如果不了解或者忘记了,就google吧。。。。
2.增强复用型的adapter,国际惯例,先看代码
public abstract class BaseFilterAdapter<T> extends BaseAdapter implements Filterable, ListCallback<T> { private CustomFilterRule<T> filter; private List<T> data; private int layoutId = -1; public BaseFilterAdapter(int layoutId) { this.layoutId = layoutId; initList(); } private void initList() { if (data == null) { data = new ArrayList<>(); } } @Override public CustomFilterRule<T> getFilter() { if (filter == null) { filter = getCostomFliter(); } return filter; } public int getLayoutId() { if (layoutId != -1) { return layoutId; } return R.layout.item_complete_textview; } @Override public int getCount() { return data.size(); } @Override public T getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { DataHodler dataHodler; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(), null); dataHodler = new DataHodler(convertView); convertView.setTag(dataHodler); } else { dataHodler = (DataHodler) convertView.getTag(); } onBindDataToView(dataHodler, getItem(position)); return convertView; } @Override public T get(int position) { return data.get(position); } @Override public void add(T t) { data.add(t); } @Override public void add(int position, T t) { data.add(position, t); } @Override public void addAll(List<T> allData) { data.addAll(allData); } @Override public void remove(T t) { data.remove(t); } @Override public void remove(int position) { data.remove(position); } @Override public int size() { return data.size(); } @Override public List<T> getData() { return data; } /** * 动态添加改变数据时,需要调用该方法重新设置Filter中的数据,否则下拉列表显示的是旧数据 */ public void onRefreshFilterData() { getFilter().setmUnfilteredData(data); } /** * 复用ViewHodler */ public class DataHodler { private View convertView; private SparseArray viewRes = new SparseArray(); public DataHodler(View convertView) { this.convertView = convertView; } /** * 获取View,提高复用性,设计知识点,抽象、View缓存 */ public <V extends View> V getView(int viewId) { V view = (V) viewRes.get(viewId); if (view == null) { view = (V) convertView.findViewById(viewId); viewRes.put(viewId, view); } return view; } } /** * 创建Filter筛选器 */ private CustomFilterRule<T> getCostomFliter() { CustomFilterRule<T> customFilter = new CustomFilterRule<T>(data) { @Override public List<T> onFilterData(String prefixString, List<T> unfilteredValues) { //在这里调用子类实现的数据过滤规则 return onFilterRule(prefixString, unfilteredValues); } @Override protected void publishResults(CharSequence constraint, Filter.FilterResults results) { data = (List<T>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } }; return customFilter; } /** * 抽象方法,绑定数据。因为不知道子类会绑定哪些数据,所以公开一个抽象方法让子类去实现数据绑定 *View */ public abstract void onBindDataToView(DataHodler hodler, T t); /** * 抽象方法,自定义数据过滤规则。作用同上 */ public abstract List<T> onFilterRule(String prefixString, List<T> unfilteredValues); }
说明:
Filterable接口: adapter必须实现该接口后再配合AutoCompleteTextView用,否则是无法筛选填充数据的
ListCallback<T>接口:该接口中的方法对应List集合部分方法实现,因为想到要提高复用性,所以直接adapter内部添加了一个List集合,之后使用该adapter就不用再添加List集合了,直接调用adapter.add(data),adapter.addAll(listData)等方法即可添加、修改数据,这样相对来说可以减少adapter使用时的代码数量,提高复用;
onFilterRule方法:抽象方法,让子类去实现自己的过滤规则,因为实际开发中,每个adapter的数据和过滤规则,不一定一样。
onBindDataToView方法:。用于绑定listView item数据
3.BaseFilterAdapter的用法
public class PersonFilterAdapter extends BaseFilterAdapter<PersonInfo> { public PersonFilterAdapter(int layoutId) { super(layoutId); } @Override public void onBindDataToView(DataHodler hodler, PersonInfo personInfo) { TextView name = hodler.getView(R.id.tv_name); TextView phone = hodler.getView(R.id.tv_phone); name.setText(personInfo.getName()); phone.setText(personInfo.getPhone()); } /** * 自定义筛选规则 * * @param unfilteredValues 要过滤的数据 * @param prefixString 关键词 */ @Override public List<PersonInfo> onFilterRule(String prefixString, List<PersonInfo> unfilteredValues) { ArrayList<PersonInfo> newValues = new ArrayList<>(); for (PersonInfo info : unfilteredValues) { if (info.getName().contains(prefixString)) { newValues.add(info); } } return newValues; } }
说明:
有没有发现,此时的adapter简洁了很多,比一般adapter的写法少了很多代码,哈哈哈
因为我们是需要对PersonInfo的名字进行数据过滤填充,所以泛型直接传PersonInfo
onFilterRule方法,在该方法中实现了自己的过滤规则,这里用的是name字段,当然同时也可以添加多个判断条件
onBindDataToView方法,绑定数据到view
4.具体的使用代码,列出几个主要的方法
@Override public void initView() { tv_seach = (AutoCompleteTextView) findViewById(R.id.tv_seach); //设置多少个字开始显示下拉列表 tv_seach.setThreshold(1); //初始化adapter,R.layout.item_complete_textview为下拉列表显示的布局文件 filterAdapter = new PersonFilterAdapter(R.layout.item_complete_textview); tv_seach.setAdapter(filterAdapter); } @Override public void initData() { //添加测试数据 for (int i = 0; i < name.length; i++) { filterAdapter.add(new PersonInfo(name[i], "13337589632" + i)); } //刷新Filter数据 filterAdapter.onRefreshFilterData(); filterAdapter.notifyDataSetChanged(); } @Override public void initListener() { //下拉列表点击事件 tv_seach.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { tv_seach.setText(filterAdapter.get(position).getName()); tv_seach.setSelection(tv_seach.getText().length());//设置光标到末尾 }
四、总结:这样设计代码,结构清晰,简单明了。同时又提高了复用性,以后如果有类似的需求,写adapter时直接继承BaseFilterAdapter,实现自己的过滤逻辑即可,无须再重新写一遍处理代码和逻辑,可以省下很大的工作量。
实例代码:https://github.com/wangzhiyuan888/SampleExample/tree/master
相关文章推荐
- Android控件使用—AutoCompleteTextView自动补全实现搜索功能
- Android自定义AutoCompleteTextView实现自动补全Email
- Android自定义AutoCompleteTextView实现自动补全Email
- Android 中 AutoCompleteTextView 自动补全功能
- 重写AutoCompleteTextView之实现自定义匹配规则 及 提示
- 自定义autoCompleteTextView实现自己的匹配规则。
- 使用AutoCompleteTextView和SharePreference实现搜索历史自动提示功能
- AutoCompleteTextView与自定义Adapter实现自动补全
- android 中使用AutoCompleteTextView 可以实现自动提示功能
- 利用AutoCompleteTextView连接到数据库实现自动提示功能
- android控件——AutoCompleteTextView(实现自动补全)
- AutoCompleteTextView 实现自定义匹配规则提示
- Android 利用AutoCompleteTextView实现模糊搜索功能,搜索结果自动提示,识别拼音首字母并转汉字提示
- AutoCompleteTextView实现邮件地址自动填充
- Android学习笔记之AutoCompleteTextView自动填充功能的案例
- Android高手进阶篇3-自定义ListView实现底部View自动隐藏和消失的功能
- 自定义View 实现 TextView 的功能:
- Android之AutoCompleteTextView自定义匹配规则
- android自定义Spinner下拉菜单和AutoCompleteTextView自动显示的(下拉列表框)样式
- 使用AutoCompleteTextView与SharedPreferences实现自动提示历史数据