某個項目須要作關鍵詞篩選自動補全的功能,而且須要對文本中間的文字也能過濾,固然作這個功能首選的控件固然是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