開篇
??AutoCompleteTextView通常配上ArrayAdapter就能解決大部分的需求蛮拔。但是ArrayAdapter是以boolean startsWith(String prefix)方式去匹配搜索項(xiàng)孽查,且沒有給我們配置關(guān)鍵字高亮。它的這種匹配規(guī)則往往不能滿足我們實(shí)際中的需求。因此称龙,我們需要進(jìn)行改造。
效果圖
ArrayAdapter源碼
ArrayAdapter
的匹配原理是同一個實(shí)現(xiàn)一個過濾器來達(dá)到過濾的效果没卸。
ArrayAdapter
源碼:
@Override
public @NonNull Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
過濾器:
class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
//我們輸入過濾就是在這個方法里實(shí)現(xiàn)。
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<>();
//這里開始循環(huán)匹配秒旋,以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) {
//這里輸出過濾后的結(jié)果约计, notifyDataSetChanged()刷新列表
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
改造
為了滿足我們的業(yè)務(wù)需求,進(jìn)行改造Gㄉ浮C喊觥!
暴露接口: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);
/**
* 過濾規(guī)則
* @param item
* @param mPrefix
* @return true细卧,此item符合過濾規(guī)則尉桩。
*/
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);
}
}
//這里我們記錄下過濾關(guān)鍵字,以便在刷新列表時高亮顯示
//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);
//我們把匹配規(guī)則用接口暴露出來給用戶止邮,讓用戶實(shí)現(xiàn)自定義化的匹配規(guī)則这橙。如果用戶沒有提供匹配規(guī)則接口,我們給一個默認(rèn)的匹配規(guī)則實(shí)現(xiàn)导披。
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);
}
}
}
//回填匹配出來的列表以及匹配到的數(shù)量屈扎。
results.values = newValues;
results.count = newValues.size();
}
return results;
}
- 3、重寫
CharSequence convertResultToString(Object resultValue)
方法:
@Override
public CharSequence convertResultToString(Object resultValue) {
//如果不重寫這個方法撩匕,默認(rèn)通過Object.toString()方法顯示鹰晨。
//而在實(shí)際的需求開發(fā)過程中,我們可能只用到Object中的某一field止毕,所以這里以接口的形式暴露并村,使得有更強(qiáng)的擴(kuò)展性。
return onItemTextListener == null ? super.convertResultToString(resultValue) : onItemTextListener.selectedItemText((T) resultValue);
}
- 4滓技、在過濾列表中高亮顯示過濾關(guān)鍵字哩牍,重寫
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);
//這里暴露高亮顯示過濾關(guān)鍵字的接口,讓用可以自由實(shí)現(xiàn)高亮顯示令漂。
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
。
篇尾
天才出于勤奮纬朝。 —— 高爾基