您的位置:首页 > 其它

AutoCompleteTextView,实现自定义规则的自动补全功能

2016-11-13 00:00 495 查看
某个项目需要做关键词筛选自动补全的功能,并且需要对文本中间的文字也能过滤,当然做这个功能首选的控件当然是AutoCompleteTextView,正常情况该控件只能对前面的字符进行匹配,如果要文本中间的文字也进行匹配就需要自己实现自定义规则了,大概搜了一下,自定义规则网上已经有了,但是相对完整的资料不多,并且大部分都是直接用ArrayAdapter配合String类型来使用,但这样使用相对来说,实用性不强,因为开发中,往往需要过滤的是class中某一个字段的数据,例如下面的实体类:

public class PersonInfo  {
private String name;
private String phone;
}

需求分析:我们需要对上面实例的name属性进行筛选过滤,如果是用ArrayAdapter,那么我们就需要用一个集合去遍历保存所有PersonInfo的name数据,然后再填充到ArrayAdapter,这样处理起来很麻烦,并且扩展性不强,如果客户要求筛选的下拉列表,要同时显示姓名和手机号,那么此时直接使用ArrayAdapter就不再满足需求了。那么最简单的办法当然是继承BaseAdapter然后自定义一个adapter了,这个adapter和我们平常自定义ListView的adapter有一点区别,因为要实现自定义过滤规则,所以必须实现Filterable接口。

一、涉及到的知识点

自定义Adapter

泛型

抽象

自定义Filterable实现数据过滤规则

二、最终效果图



三、实现代码

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);
}

说明:

这里用到了抽象和泛型,主要是为了提高代码的复用性(Tabstract),如果不了解或者忘记了,就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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: