Android AutoCompleteTextView之高亮關(guān)鍵字解決方案

開篇

??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

篇尾

天才出于勤奮纬朝。 —— 高爾基

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末收叶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子共苛,更是在濱河造成了極大的恐慌判没,老刑警劉巖蜓萄,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異澄峰,居然都是意外死亡嫉沽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門俏竞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绸硕,“玉大人,你說我怎么就攤上這事魂毁〔E澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵席楚,是天一觀的道長夺蛇。 經(jīng)常有香客問我,道長酣胀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任娶聘,我火速辦了婚禮闻镶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丸升。我一直安慰自己铆农,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布狡耻。 她就那樣靜靜地躺著墩剖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夷狰。 梳的紋絲不亂的頭發(fā)上岭皂,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音沼头,去河邊找鬼爷绘。 笑死,一個胖子當(dāng)著我的面吹牛进倍,可吹牛的內(nèi)容都是我干的土至。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼猾昆,長吁一口氣:“原來是場噩夢啊……” “哼陶因!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起垂蜗,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤楷扬,失蹤者是張志新(化名)和其女友劉穎解幽,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毅否,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亚铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了螟加。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徘溢。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捆探,靈堂內(nèi)的尸體忽然破棺而出然爆,到底是詐尸還是另有隱情,我是刑警寧澤黍图,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布曾雕,位于F島的核電站,受9級特大地震影響助被,放射性物質(zhì)發(fā)生泄漏剖张。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一揩环、第九天 我趴在偏房一處隱蔽的房頂上張望搔弄。 院中可真熱鬧,春花似錦丰滑、人聲如沸顾犹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炫刷。三九已至,卻和暖如春郁妈,著一層夾襖步出監(jiān)牢的瞬間浑玛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工噩咪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锄奢,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓剧腻,卻偏偏與公主長得像拘央,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子书在,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344