自定義控制元件實踐-帶特效的索引欄

張大仙發表於2019-02-18

效果預覽

jdfw.gif
jdfw.gif

前言

上一段時候面試了一些人, 問到通訊錄中側邊的快速索引這個怎麼實現. 沒想到好幾個人都回答不上來, 看到他們簡歷上寫的熟悉掌握自定義控制元件 (汗 ==!) . 想到以前寫的帶特效的快速索引欄, 點選的時候該區域就會彈起,效果還是不錯的。 下面一步步分析應該怎麼實現這個控制元件。

分析需求

自定義控制元件先看分析靜態的。a~z 怎麼繪製呢~ 有的人居然回答是用ListView來做… ((⊙o⊙)) . 因為a~z是要在一個地方繪製的. 不是什麼列表. 所以應該想到用是用canvas.drawText()方法. 還有這個應該是繼承View而不是ViewGroup.因為a~z是在一個View上的.

靜態view分析實現.

最關鍵的api是canvas.drawText(letter, x, y, paint); 這個方法可以指定繪製什麼. 同時可以指定繪製的位置.可以知道這些字母繪製的時候橫座標應該不變化. 變化的是縱座標. 而且縱座標.是有規律的變化

動態view分析實現

所謂的動態就是 點選的時候這個區域內的字母要向左偏移. 並且相鄰的程式碼要也要有不同程度的左偏移. 點選的時候應該去考慮在onTouchEvent中獲取x y座標 ,通過座標去判斷哪個字母應該做什麼效果. 當放手的時候 這個已經偏移的view不會立馬恢復到初始狀態. 而是要有一定時間. 這個怎麼實現呢~ 有兩種思路 .
第一: 通過當Scroller這個類去模擬資料變化. 這個一般用的少.我用這種寫法 鞏固一下基礎~~
還有一種思路 就是利用屬性動畫.指定值的屬性 然後也可以得到一定時間內變化的值~

關鍵的程式碼

繪製onDraw();

 @Override
    protected void onDraw(Canvas canvas) {
        // 遍歷26個英文字母, 計算座標, 進行繪製
        for (int i = 0; i < LETTERS.length; i++) {
            String letter = LETTERS[i];

            // 計算x座標
            float dx = 0;
            //float x = cellWidth * 0.5f - paint.measureText(letter) * 0.5f + 80;
            //  float x = cellWidth - paint.measureText(letter);
            float x = cellWidth - measureText * 1.5f;
            if (!isUp) {
                mX = 1.0f;
            }
            switch (currentIndex - i) {
                case -4:
                    dx = measureText * 1.5f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_4));
                    setTextSize(27);
                    break;
                case -3:
                    dx = measureText * 2.5f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_3));
                    setTextSize(33);
                    break;
                case -2:
                    dx = measureText * 4.0f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_2));
                    setTextSize(38);
                    break;
                case -1:
                    dx = measureText * 5.0f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_1));
                    setTextSize(42);
                    break;
                case 0:
                    paint.setColor(getResources().getColor(R.color.text_blue_search));
                    dx = measureText * 6.0f * mX;
                    setTextSize(50);
                    break;
                case 1:
                    dx = measureText * 5.0f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_1));
                    setTextSize(42);
                    break;
                case 2:
                    dx = measureText * 4.0f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_2));
                    setTextSize(38);
                    break;
                case 3:
                    dx = measureText * 2.5f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_2));
                    setTextSize(33);
                    break;
                case 4:
                    dx = measureText * 1.5f * mX;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_3));
                    setTextSize(28);
                    break;
                default:
                    dx = 0;
                    paint.setColor(getResources().getColor(R.color.text_blue_search_4));
                    setTextSize(26);
                    break;
            }
            if (!isUp) { //觸控狀態
                x -= dx;
            } else {  //鬆手狀態
                x -= dx;
                paint.setColor(getResources().getColor(R.color.text_blue_search));
                setTextSize(26);
            }

            // 計算y座標
            Rect bounds = new Rect();
            // 獲取文字的矩形區域
            paint.getTextBounds(letter, 0, letter.length(), bounds);
            float y = cellHeight * 0.5f + bounds.height() * 0.5f + i * cellHeight;

            // 繪製文字
            if (letter.equals("I")) {
                x += measureText * 0.2f;
            }
            canvas.drawText(letter, x, y, paint);
        }
    }複製程式碼

處理點選的操作:

 @Override
    public boolean onTouchEvent(MotionEvent event) {

        float y;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                scroller.forceFinished(true);
                isUp = false;
                setPadding(500, 0, 0, 0);
                // 獲取被點選到的字母索引
                y = event.getY();
                // 根據y值, 計算當前按下的字母位置
                currentIndex = (int) (y / cellHeight);
                if (currentIndex != lastIndex) {
                    if (currentIndex >= 0 && currentIndex < LETTERS.length) {
                        String letter = LETTERS[currentIndex];
                        if (onLetterUpdateListener != null) {
                            onLetterUpdateListener.onLetterUpdate(letter);
                        }
                        // 記錄上一次觸控的字母
                        lastIndex = currentIndex;
                    }
                }

                break;
            case MotionEvent.ACTION_MOVE:
                // 獲取被點選到的字母索引
                y = event.getY();
                // 根據y值, 計算當前按下的字母位置
                currentIndex = (int) (y / cellHeight);
                if (currentIndex != lastIndex) {
                    if (currentIndex >= 0 && currentIndex < LETTERS.length) {
                        String letter = LETTERS[currentIndex];
                        if (onLetterUpdateListener != null) {
                            onLetterUpdateListener.onLetterUpdate(letter);
                        }
                        // 記錄上一次觸控的字母
                        lastIndex = currentIndex;
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                isUp = true;
                lastIndex = -1;
                //呼叫回撥裡面的方法
                if (null != onUpListener) {
                    onUpListener.up();
                }
                smaooth();

                break;
            default:
                break;
        }
        invalidate();
        return true;
    }複製程式碼

總結:

自定義控制元件其實不難 看到需求之後, 先別忙著寫. 先好好分析應該怎麼實現是最高簡潔的. 然後先實現靜態的view 然後再考慮動態的變化. 分析清楚了, 程式碼就好寫了.~~
最後奉上Demo地址: github.com/zmin666/Qui…
如果對你有幫助, 歡迎star~~

相關文章