自定義索引View

不論天氣如何變化,該擼碼還是要擼的.近期打算好好學習一下自定義View的知識.

這裏寫圖片描述

這是一個自定義的索引條目,雖然網上很多教程,但是紙上來的終覺淺,還是自己寫一下.體會深刻,也會學到更多知識.

廢話不說,代碼以下

public class SpeedIndexView extends View {

    private String[] letters = {"A", "B", "C", "D",
            "E", "F", "G", "H",
            "I", "J", "K", "L",
            "M", "N", "O", "P",
            "Q", "R", "S", "T",
            "U", "V", "W", "X",
            "Y", "Z"};
    private Paint paint;//畫筆
    private Rect rect;
    private int width;
    private float letterLength;
    private int lastIndex = -1;
    private TouchIndexListener onIndexListener;

    public SpeedIndexView(Context context) {
        this(context, null);
    }

    public SpeedIndexView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpeedIndexView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //抗鋸齒
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        //字體
        paint.setTypeface(Typeface.DEFAULT_BOLD);
        paint.setTextSize(DimensUtils.dip2px(context, 16));
        rect = new Rect();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public SpeedIndexView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //儘量不要做創建對象的操作,防止內存頻繁回收,造成界面卡頓
        for (int i = 0; i < letters.length; i++) {
            paint.getTextBounds(letters[i], 0, letters[i].length(), rect);
            float measureText = paint.measureText(letters[i]);//獲取字符串的寬度
            float x = width / 2.0f - measureText / 2.0f;
            float y = letterLength + letterLength * i;
            canvas.drawText(letters[i], x, y, paint);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //View的Size變化時被調用
        //在onDraw中無法獲得準確的寬高
//        width = getMeasuredWidth();
//        height = getMeasuredHeight();
        width = w;
        Log.d("view", "w" + w + "h" + h + "oldw" + oldw + "oldh" + oldh);
        //每個字符串的高度
        letterLength = h * 1.0f / letters.length;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                measuerAndWhitch(event);
                break;
            case MotionEvent.ACTION_MOVE:
                measuerAndWhitch(event);
                break;
            case MotionEvent.ACTION_UP:
                lastIndex = -1;
                break;
        }
        return true;
    }

    private void measuerAndWhitch(MotionEvent event) {
        float y = event.getY();
        int index = (int) (y / letterLength);
        Log.d("view", "" + index);
        //與上次觸摸不同
        if (lastIndex != index) {
            if (index >= 0 && index < letters.length) {
                if (onIndexListener != null)
                    onIndexListener.onTouchindexListener(letters[index]);
            }
            lastIndex = index;
        }

    }

    public void setOnTouchIndexListener(TouchIndexListener indexListener) {
        this.onIndexListener = indexListener;

    }

    public interface TouchIndexListener {
        void onTouchindexListener(String letter);
    }
}

1, 繼承View,重寫3個構造方法,第四個構造方法需要api21以上可以使用,具體每個構造方法先不深究.

2, 初始畫筆,設置畫筆

3,在onDraw中,使用canvas.drawText方法,傳入內容,x,y座標,和畫筆.需要注意的是這個x和y是字體左下角的座標,並不是中心點

4,要計算出每個字母的位置,可以使用畫筆去測量這個字符串的寬度.

5,在onDraw中儘量不要創建過多的對象,因爲onDraw過程中創建過多的對象,會造成界面的卡頓.

6,重寫onTouchEvent方法,返回true,攔截事件,處理點擊事件和滑動以及擡起事件.

7,回調接口,將選中的字符串傳遞出去,以便調用時對選中的字符串進行其他處理,比如ListView選項.

Tips:該自定義View可以熟悉自定義View的流程,畫筆,畫布的使用等.還可以經過修改使其動態顯示右側列表選項,但是要注意在數據返回設置給View之前,不能讓View進行顯示.否則會產生Null.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章