开篇
AutoCompleteTextView通常配上ArrayAdapter就能解决大部分的需求。但是ArrayAdapter是以boolean startsWith(String prefix)方式去匹配搜索项,且没有给我们配置关键字高亮。它的这种匹配规则往往不能满足我们实际中的需求。因此,我们需要进行改造。
效果图
ArrayAdapter源码
ArrayAdapter
的匹配原理是同一个实现一个过滤器来达到过滤的效果。
ArrayAdapter
源码:
@Override
public @NonNull Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
过滤器:
class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
//我们输入过滤就是在这个方法里实现。
final FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
final ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
final String prefixString = prefix.toString().toLowerCase();
final ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<>();
//这里开始循环匹配,以startsWith()方式匹配
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
for (String word : words) {
if (word.startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//这里输出过滤后的结果, notifyDataSetChanged()刷新列表
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
改造
为了满足我们的业务需求,进行改造!!!
暴露接口:OnItemTextListener
接口
public interface OnItemTextListener<T> {
/**
* 获取T对象中的文本
* @param item
* @return
*/
CharSequence selectedItemText(@Nullable T item);
/**
* 过滤列表item的显示文本
* @param item
* @param mPrefix
* @return
*/
CharSequence ListItemText(@Nullable T item, String mPrefix);
/**
* 过滤规则
* @param item
* @param mPrefix
* @return true,此item符合过滤规则。
*/
boolean filterItem(@NonNull T item, String mPrefix);
}
- 1、我们把
ArrayAdapter
拷贝一份,命名为FilterAdapter
:
public class FilterAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
private String mPrefix = "";
private OnItemTextListener<T> onItemTextListener = null;
public void setOnItemTextListener(OnItemTextListener<T> onItemTextListener) {
this.onItemTextListener = onItemTextListener;
}
//此处为省略部分
}
- 2、重写
performFiltering(CharSequence prefix)
方法:
@Override
protected FilterResults performFiltering(CharSequence prefix) {
final FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<>(mObjects);
}
}
//这里我们记录下过滤关键字,以便在刷新列表时高亮显示
//add custom
mPrefix = prefix == null ? "" : prefix.toString().trim();
if (mPrefix.length() == 0) {
final ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
final String prefixString = mPrefix.toLowerCase();
final ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
//我们把匹配规则用接口暴露出来给用户,让用户实现自定义化的匹配规则。如果用户没有提供匹配规则接口,我们给一个默认的匹配规则实现。
if (onItemTextListener != null) {
if (onItemTextListener.filterItem(value, prefixString))
newValues.add(value);
} else {
final String valueText = value.toString().toLowerCase();
if (valueText.contains(prefixString.toLowerCase())) {
newValues.add(value);
}
}
}
//回填匹配出来的列表以及匹配到的数量。
results.values = newValues;
results.count = newValues.size();
}
return results;
}
- 3、重写
CharSequence convertResultToString(Object resultValue)
方法:
@Override
public CharSequence convertResultToString(Object resultValue) {
//如果不重写这个方法,默认通过Object.toString()方法显示。
//而在实际的需求开发过程中,我们可能只用到Object中的某一field,所以这里以接口的形式暴露,使得有更强的扩展性。
return onItemTextListener == null ? super.convertResultToString(resultValue) : onItemTextListener.selectedItemText((T) resultValue);
}
- 4、在过滤列表中高亮显示过滤关键字,重写
Private View createViewFromResource(@NonNull LayoutInflater inflater, int position, @Nullable View convertView, @NonNull ViewGroup parent, int resource)
方法:
View createViewFromResource(@NonNull LayoutInflater inflater, int position,
@Nullable View convertView, @NonNull ViewGroup parent, int resource) {
//省略部分
final T item = getItem(position);
//这里暴露高亮显示过滤关键字的接口,让用可以自由实现高亮显示。
CharSequence value = onItemTextListener == null ? (item == null ? "" : item.toString()) : onItemTextListener.ListItemText(item, mPrefix);
text.setText(value);
return view;
}
👌,到这里改造完成。下面说说使用。
使用
private AutoCompleteTextView autoCompleteTextView;
FilterAdapter<T> adapter = new FilterAdapter<>(getContext(), R.layout.common_drop_down_item_layout, R.id.tv_content, list);
adapter.setOnItemTextListener(new FilterAdapter.OnItemTextListener<T>() {
@Override
public CharSequence selectedItemText(@Nullable T item) {
return item == null ? "" : item.getEarNo();
}
@Override
public CharSequence ListItemText(@Nullable T item, String mPrefix) {
Log.i(TAG, "ListItemText: " + mPrefix);
String value = item == null ? "" : item.getEarNo();
if (mPrefix.length() > 0) {
int index = value.indexOf(mPrefix);
if (index == -1)
index = value.toLowerCase().indexOf(mPrefix.toLowerCase());
if (index > -1) {
return SpannableUtil.setTextColor(value, index, index + mPrefix.length(), Color.GREEN);
}
}
return value;
}
@Override
public boolean filterItem(@NonNull T item, String mPrefix) {
final String valueText = item.getEarNo().toLowerCase();
return valueText.contains(mPrefix.toLowerCase());
}
});
autoCompleteTextView.setAdapter(adapter);
public static SpannableString setTextColor(String text, int start, int end, int color) {
SpannableString spannable = new SpannableString(text);
spannable.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannable;
}
最后附上源码:
FilterAdapter
微信:eoy9527
、QQ:1006368252
。
篇尾
天才出于勤奋。 —— 高尔基