某个项目须要作关键词筛选自动补全的功能,而且须要对文本中间的文字也能过滤,固然作这个功能首选的控件固然是AutoCompleteTextView,正常状况该控件只能对前面的字符进行匹配,若是要文本中间的文字也进行匹配就须要本身实现自定义规则了,大概搜了一下,自定义规则网上已经有了,可是相对完整的资料很少,而且大部分都是直接用ArrayAdapter配合String类型来使用,但这样使用相对来讲,实用性不强,由于开发中,每每须要过滤的是class中某一个字段的数据,例以下面的实体类:git
public class PersonInfo { private String name; private String phone; }
需求分析:咱们须要对上面实例的name属性进行筛选过滤,若是是用ArrayAdapter,那么咱们就须要用一个集合去遍历保存全部PersonInfo的name数据,而后再填充到ArrayAdapter,这样处理起来很麻烦,而且扩展性不强,若是客户要求筛选的下拉列表,要同时显示姓名和手机号,那么此时直接使用ArrayAdapter就再也不知足需求了。那么最简单的办法固然是继承BaseAdapter而后自定义一个adapter了,这个adapter和咱们日常自定义ListView的adapter有一点区别,由于要实现自定义过滤规则,因此必须实现Filterable接口。github
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); }
说明:ide
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); }
说明:测试
3.BaseFilterAdapter的用法this
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; } }
说明:google
4.具体的使用代码,列出几个主要的方法spa
@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());//设置光标到末尾 }
4、总结:这样设计代码,结构清晰,简单明了。同时又提升了复用性,之后若是有相似的需求,写adapter时直接继承BaseFilterAdapter,实现本身的过滤逻辑便可,无须再从新写一遍处理代码和逻辑,能够省下很大的工做量。设计
实例代码:https://github.com/wangzhiyuan888/SampleExample/tree/master