自定义View(3) -- 字母索引

效果图:


自定义View(3) -- 字母索引_第1张图片
字母索引

自定义view的流程,具体请点此查看:自定义view套路
我们先重写构造器,然后重写onMeasure函数进行测量设置宽高,在本例中,宽我是根据padding和测量一个字母w的宽度来设置的,高度就是默认的,因为一般是设置为match_parent.

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

    public AlphabeticalIndexView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AlphabeticalIndexView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = (int) (getPaddingLeft() + getPaddingRight() + DisplayUtil.getTextWidth("W", mPaint));
        int h = getMeasuredHeight();
        setMeasuredDimension(w, h);
    }


    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(DisplayUtil.sp2px(context, 15));
    }

因为不是viewGroup,所以不必重写onLayout函数,我们直接进入onDraw

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 每个字母所占用的高度
        for (int i = 0; i < mLetters.length; i++) {
            String letter = mLetters[i];
            if (letter.equals(mCurrentTouchLetter))
                mPaint.setColor(Color.BLUE);
            else
                mPaint.setColor(Color.GRAY);

            // 获取字体的宽度
            float measureTextWidth = mPaint.measureText(letter);
            // 获取内容的宽度
            int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight();

            canvas.drawText(letter, getPaddingLeft() + (contentWidth - measureTextWidth) / 2, getPaddingTop()  + mSingLetterHeight * i + DisplayUtil.getTextBaseLine(mPaint), mPaint);
        }
    }

这里可能有点疑惑,为什么要用内容的宽度减去字体的宽度然后除于2,这是因为每个字母的宽度都不一样,如果不这样做的画,那么比如I就会和A W等字母左对齐,我们这样做的目的就是为了都居中。
这样我们就实现了一个静态的页面没我们要让他触摸改变,我们就重写onTouchEvent进行操作,并且添加相应的回调。

 @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                // 获取当前手指触摸的Y位置
                float fingerY = ev.getY();
                int pos = (int) (fingerY / mSingLetterHeight);
                if (pos > -1 && pos < mLetters.length && !mLetters[pos].equals(mCurrentTouchLetter)) {
                    mCurrentTouchLetter = mLetters[pos];
                    triggerTouchListener(true);
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                triggerTouchListener(false);
                break;
        }

        return true;
    }

    private void triggerTouchListener(boolean isTouch) {
        if (mTouchListener != null)
            mTouchListener.onTouch(mCurrentTouchLetter, isTouch);
    }

    // 设置触摸监听
    private SideBarTouchListener mTouchListener;

    public void setOnSideBarTouchListener(SideBarTouchListener touchListener) {
        this.mTouchListener = touchListener;
    }

    public interface SideBarTouchListener {
        void onTouch(String letter, boolean isTouch);
    }

这样我们就可以实现了这个功能点。
优化思路
一个view绘制出来我们还需要进行优化,这一步是非常重要的。在本例中,我们的优化主要是在 onTouchEvent里面,因为我们知道,invalidate函数是一个一个做了很多事情的函数,具体请看:Android invalidate流程分析 我们要减少它的调用,所以我们应该先判断一下,如果当前的这个索引和触摸的这个不一样的话才调用invalidate,这样的话就优雅很多了。在本例中我没有将需要改变的属性,比如字体大小、选中颜色、默认颜色等提取在attr里面,需要的请自行添加。
参考:Android字母索引列表

源码github下载地址:
https://github.com/ChinaZeng/CustomView

你可能感兴趣的:(自定义View(3) -- 字母索引)