仿微信@功能

最近項目需要做類似微信的@ 功能拷橘,在網(wǎng)上找了很多舞虱,發(fā)現(xiàn)基本都是使用Span 來實現(xiàn)的睦番,覺得寫的比較好的就是這篇
Android 如何優(yōu)雅地實現(xiàn)@人功能类茂?——仿微博、仿QQ托嚣、仿微信巩检、零入侵、高擴(kuò)展性
由于本人對kotlin 的理解不深示启,有些代碼看不太懂兢哭,自己根據(jù)這個思路用java 寫了一遍

首先,我們給 EditText 設(shè)置一個 Span 用來標(biāo)示這個一個整體夫嗓,代碼如下

/**
     * 添加 @內(nèi)容
     *
     * @param text 包含 @ 符號的字符
     */
    public void addSpan(String text) {
        //將 @字符串插入到 光標(biāo)之后
        getText().insert(getSelectionEnd(), text);
        //創(chuàng)建一個數(shù)據(jù)類迟螺,由于后面尋找對應(yīng)的@ 內(nèi)容
        DataSpan myTextSpan = new DataSpan(); 
        //設(shè)置Span
        getText().setSpan(myTextSpan, getSelectionEnd() - text.length(), getSelectionEnd(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
       //將光標(biāo)設(shè)置到最后
        setSelection(getText().length());
    }

設(shè)置好了 Span 字符實現(xiàn)了在 EditText 中插入了@ 字符串,下面需要對 @ 字符串進(jìn)行整體刪除舍咖,監(jiān)聽 EditText 的 setOnKeyListener 事件:

        //該事件每次刪除一個字符都會回調(diào)一次
        mEditText.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                //  判斷是否為 按下刪除 事件
                if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                    //做整體刪除處理
                    return CopyWeChatEditText.KeyDownHelper(mEditText.getText());
                }
                return false;
            }
        });

    /**
     * 找到最后 Span 塊
     *
     * @param text
     * @return
     */
    public static boolean KeyDownHelper(Editable text) {
        //找到光標(biāo)開始結(jié)束坐標(biāo)
        int selectionEnd = Selection.getSelectionEnd(text);
        int selectionStart = Selection.getSelectionStart(text);
        //獲取 EditText 中所有的 Span 通過 DataSpan 綁定是的類型
        DataSpan[] spans = text.getSpans(selectionStart, selectionEnd, DataSpan.class);
        for (DataSpan span : spans) {
            if (span != null) {
                //找到第一個非空的 span 和該 span 對應(yīng) EditText 中的開始結(jié)束位置
                int spanStart = text.getSpanStart(span);
                int spanEnd = text.getSpanEnd(span);
                //假如光標(biāo)的位置位于該 span 中的最后一位矩父,即位于@ 字符串后面
                if (selectionEnd == spanEnd) {
                    //設(shè)置選中該 span 字符串
                    Selection.setSelection(text, spanStart, spanEnd);
                    return false;
                }
            }
        }
        return false;
    }

注意 Selection.setSelection(text, spanStart, spanEnd); 這一行代碼,效果就相當(dāng)于長按后選擇字符串谎仲,setOnKeyListener 事件的調(diào)用時機是鍵盤按下刪除浙垫,但字符串還沒有做刪除操作刨仑,所以設(shè)置選中郑诺,在執(zhí)行刪除操作時候就能刪除選中的所有字符,實現(xiàn)@ 內(nèi)容整塊刪除效果

現(xiàn)在基本實現(xiàn)了 @ 功能和整塊刪除效果杉武,但是還有一點小問題辙诞,假如自己手動將光標(biāo)移動到 @字符串中間,然后在刪除字符轻抱,當(dāng)將@字符刪除飞涂,在將光標(biāo)移動到 @字符串之后,再點擊刪除祈搜,發(fā)現(xiàn)整個字符還是被刪除了较店。

要解決這個方法,需要監(jiān)聽 span 字符串是否包含 @ 字符

//設(shè)置工廠容燕,用來監(jiān)聽 span 字符串變化
     setEditableFactory(new NoCopySpanEditableFactory(new DirtySpanWatcher()));


    class NoCopySpanEditableFactory extends Editable.Factory {

        private NoCopySpan spans;

        public NoCopySpanEditableFactory(NoCopySpan spans) {
            this.spans = spans;
        }

        @Override
        public Editable newEditable(CharSequence source) {
            //添加 span 字符串的監(jiān)聽
            SpannableStringBuilder stringBuilder = new SpannableStringBuilder(source);
            stringBuilder.setSpan(spans, 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            return stringBuilder;
        }
    }


    class DirtySpanWatcher implements SpanWatcher {

        @Override
        public void onSpanAdded(Spannable text, Object what, int start, int end) {

        }

        @Override
        public void onSpanRemoved(Spannable text, Object what, int start, int end) {

        }

        @Override
        public void onSpanChanged(Spannable text, Object what, int ostart, int oend, int nstart, int nend) {
            //span 字符串改變時候監(jiān)聽
            int spanEnd = text.getSpanEnd(what);
            int spanStart = text.getSpanStart(what);
            if (spanStart >= 0 && spanEnd >= 0 && what instanceof DataSpan) {
                CharSequence charSequence = text.subSequence(spanStart, spanEnd);
                //刪除后的字符是否包含 @ 字符
                if (!charSequence.toString().contains("@")) {
                    DataSpan[] spans = text.getSpans(spanStart, spanEnd, DataSpan.class);
                    for (DataSpan span : spans) {
                        if (span != null) {
                            //不包含 @ 字符梁呈,將該 span 屬性去除
                            text.removeSpan(span);
                            break;
                        }
                    }
                }
            }
        }
    }

到這里,仿微信的@ 功能已經(jīng)完成蘸秘,至于為什么會有這兩個類官卡,請看轉(zhuǎn)載的文章蝗茁,如果您有更好的監(jiān)聽方法,請告知我寻咒,謝謝
最后附上Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哮翘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子毛秘,更是在濱河造成了極大的恐慌饭寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熔脂,死亡現(xiàn)場離奇詭異佩研,居然都是意外死亡,警方通過查閱死者的電腦和手機霞揉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門旬薯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人适秩,你說我怎么就攤上這事绊序。” “怎么了秽荞?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵骤公,是天一觀的道長。 經(jīng)常有香客問我扬跋,道長阶捆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任钦听,我火速辦了婚禮洒试,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘朴上。我一直安慰自己垒棋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布痪宰。 她就那樣靜靜地躺著叼架,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衣撬。 梳的紋絲不亂的頭發(fā)上乖订,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音具练,去河邊找鬼乍构。 笑死,一個胖子當(dāng)著我的面吹牛靠粪,可吹牛的內(nèi)容都是我干的蜡吧。 我是一名探鬼主播毫蚓,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昔善!你這毒婦竟也來了元潘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤君仆,失蹤者是張志新(化名)和其女友劉穎翩概,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體返咱,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡钥庇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了咖摹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片评姨。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖萤晴,靈堂內(nèi)的尸體忽然破棺而出吐句,到底是詐尸還是另有隱情,我是刑警寧澤店读,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布嗦枢,位于F島的核電站,受9級特大地震影響屯断,放射性物質(zhì)發(fā)生泄漏文虏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一殖演、第九天 我趴在偏房一處隱蔽的房頂上張望氧秘。 院中可真熱鬧,春花似錦剃氧、人聲如沸敏储。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至妥箕,卻和暖如春滥酥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畦幢。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工坎吻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宇葱。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓瘦真,卻偏偏與公主長得像刊头,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诸尽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容