1. 程式人生 > >高仿SinaWeibo新浪微博釋出頁面話題效果

高仿SinaWeibo新浪微博釋出頁面話題效果

最近做了一個仿新浪微博話題效果的功能,網上搜索了幾個效果,都存在一定問題,最終借鑑別人的思路,完成這一套效果.

首先,我們拆分邏輯以及開發順序.

1,實現話題變色效果
2,實現插入話題效果
3,實現話題選中刪除效果
4,實現點選話題,游標在話題之後

這裡寫圖片描述

下面我們就一步一步實現效果.

一,實現話題變色

實現邏輯主要是通過EditTextaddTextChangedListener()來進行監聽文字變動,通過正則表示式來匹配出文字中的話題.

利用正則表示式獲取全部話題:
//正則表示式,一定要和伺服器以及 iOS 端統一
private
static final String topicRegex = "#([^#]+?)#"; public static ArrayList<String> findTopic(String s) { Pattern p = Pattern.compile(topicRegex); Matcher m = p.matcher(s); ArrayList<String> list = new ArrayList<>(); while (m.find()) { list.add(m.group()); } return
list; }
通過正則表示式匹配出的話題集合,遍歷出每個話題的 startIndex 位置(後來發現Matcher方法是有獲取 index 的方法),並且通過EditText.getText()方法獲取的Editable直接對文字進行操作:
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
        Log.i("MainActivity", "onTextChanged");
        if (TextUtils.isEmpty(s)) return
; //1,查詢話題 String content = s.toString(); mTopicList.clear(); mTopicList.addAll(findTopic(s.toString())); //2,為查找出的變色 //首先要為editable,去除之前設定的colorSpan Editable editable = mEditText.getText(); for (int i = 0; i < mColorSpans.size(); i++) { editable.removeSpan(mColorSpans.get(i)); } mColorSpans.clear(); //為editable,中的話題加入colorSpan int findPos = 0; int size = mTopicList.size(); for (int i = 0; i < size; i++) {//便利話題 String topic = mTopicList.get(i); findPos = content.indexOf(topic, findPos); if (findPos != -1) { ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.BLUE); editable.setSpan(colorSpan, findPos, findPos = findPos + topic.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mColorSpans.add(colorSpan); } } }

二,實現插入話題效果

新浪微博的插入話題效果,是開啟一個新的頁面,點選一個話題後,插入游標所在的位置.這裡我用一個按鈕模仿了這個動作.要注意的是,要記錄插入之前游標的位置,並且在插入話題後,把游標放置在話題之後:

if (v.getId() == R.id.button) {
            //插入話題
            int selectionStart = mEditText.getSelectionStart();
            //下面這些操作也可以直接替換為操作 EditText 的Editable實現,下面的程式碼 review 後看起來由點蠢,直接呼叫 insert 方法比較巧妙
            String content = mEditText.getText().toString();

            String firstStr = content.substring(0, selectionStart);
            String secondStr = content.substring(selectionStart, content.length());

            String insertTopic = "#這是一個插入的話題#";
            mEditText.setText(firstStr + insertTopic + secondStr);
            mEditText.setSelection(selectionStart + insertTopic.length());
}

三,實現話題選中刪除效果

這裡也是要時時判斷游標所在的位置,當光標出現在話題之後,再次點選刪除,就截獲為選中話題效果,實現起來也是很簡單的.

但是要注意的是,我們不能利用 activity 裡面的onKeyDown()onKeyUp()兩個回撥,通過 log 發現文字變動和按鍵點選的回撥順序為beforeTextChanged->onTextChanged->afterTextChanged->onKeyDown->onKeyUp.

這也說明了如果通過 攔截onKeyDown()onKeyUp()兩個回撥時,文字是已經刪除之後的文字,並能有效的達到我們要實現的目的,那麼有沒有是文字改變之前就能擷取到按鍵的方法呢?

其實我們可以通過監聽EditTextsetOnKeyListener()方法來監聽按鍵(onKey->beforeTextChanged->onTextChanged->afterTextChanged->onKeyDown->onKeyUp):

@Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        Log.i("MainActivity", "onKey");
        if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN && mCheckBox2.isChecked()) {

            int selectionStart = mEditText.getSelectionStart();
            int selectionEnd = mEditText.getSelectionEnd();
            //如果游標起始和結束在同一位置,說明是選中效果,直接返回 false 交給系統執行刪除動作
            if (selectionStart != selectionEnd) {
                return false;
            }

            Editable editable = mEditText.getText();
            String content = editable.toString();
            int lastPos = 0;
            int size = mTopicList.size();
            //遍歷判斷游標的位置
            for (int i = 0; i < size; i++) {
                String topic = mTopicList.get(i);
                lastPos = content.indexOf(topic, lastPos);
                if (lastPos != -1) {
                    if (selectionStart != 0 && selectionStart >= lastPos && selectionStart <= (lastPos + topic.length())) {
                        //選中話題
                        mEditText.setSelection(lastPos, lastPos + topic.length());
                        return true;
                    }
                }
                lastPos += topic.length();
            }
        }
        return false;
    }

四,實現點選話題,游標在話題之後

邏輯處理就是監聽EditText的點選事件,處理游標所在位置,如果在位置在話題內,立即放置在話題的後面,邏輯比較簡單,直接上程式碼:

if (v.getId() == R.id.editText && mCheckBox1.isChecked()) {
            int selectionStart = mEditText.getSelectionStart();

            int lastPos = 0;
            int size = mTopicList.size();
            for (int i = 0; i < size; i++) {
                String topic = mTopicList.get(i);
                lastPos = mEditText.getText().toString().indexOf(topic, lastPos);

                if (lastPos != -1) {
                    if (selectionStart >= lastPos && selectionStart <= (lastPos + topic.length())) {
                        //在這position 區間就移動游標
                        mEditText.setSelection(lastPos + topic.length());
                    }
                }
                lastPos = lastPos + topic.length();
            }
        }

最終還是有點小遺憾沒有完成.當拷貝一段已經變色的話題,再貼上到文字後,無法刪除掉拷貝進來文字的變色效果,嘗試呼叫Editable.clearSpans()去除全部 span 也無法實現,並且這個方法會導致直接卡死.後來直接使用EditText.setText()控制文字,確實達到了效果,但是當輸入法是類似 mx4預設輸入法效果時(效果大概是,輸入的拼音直接顯示在 EditText 裡面,點選被選漢字,替換掉拼音),會把輸入的拼音放入到文本里,所以上面的程式碼,我一般是直接操作 Editable.

如果有更好的方法或者修復了上面的小遺憾,請留言指教.