美文网首页Android 进阶技术篇专题
Android AutoCompleteTextView之高亮关

Android AutoCompleteTextView之高亮关

作者: SwitchLife | 来源:发表于2019-03-13 17:15 被阅读2次

    开篇

      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

    微信:eoy9527QQ:1006368252

    篇尾

    天才出于勤奋。 —— 高尔基

    相关文章

      网友评论

        本文标题:Android AutoCompleteTextView之高亮关

        本文链接:https://www.haomeiwen.com/subject/ovhtmqtx.html