美文网首页
列表字母侧边指示器效果

列表字母侧边指示器效果

作者: 刘孙猫咪 | 来源:发表于2018-03-04 21:42 被阅读0次
    device-2018-03-04-205329.gif

    看到这个效果肯定想到的是要自定义View来实现;

    public class LitterSideBar extends View {
        private Paint mPaint;
        //被选画笔
        private Paint choicePaint;
        //触摸到的字母
        private String mCurrentTouchLetter = "";
        //定义26个字母
        private String[] litterArray = {"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 int normalTextColor = Color.BLACK;
        //被选画笔颜色
        private int touchTextColor = Color.RED;
        //字体大小
        private int litterTextSize = 15;
    
        public LitterSideBar(Context context) {
            this(context, null);
        }
    
        public LitterSideBar(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public LitterSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //初始化自定义属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LitterSideBar);
            litterTextSize = array.getDimensionPixelSize(R.styleable.LitterSideBar_litterTextSize, (int) sp2px(litterTextSize));
            normalTextColor = array.getColor(R.styleable.LitterSideBar_normalTextColor, normalTextColor);
            touchTextColor = array.getColor(R.styleable.LitterSideBar_choiceTextColor, touchTextColor);
    
            mPaint = getPaint(normalTextColor);
            choicePaint = getPaint(touchTextColor);
            array.recycle();
    
        }
    
        /**
         * 初始化画笔
         *
         * @param color
         * @return
         */
        private Paint getPaint(int color) {
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setTextSize(litterTextSize);
            paint.setColor(color);
            return paint;
        }
    
        private float sp2px(int sp) {
            return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
        }
    }
    

    上面就是一些自定义属性、画笔的一些初始化及一些常量和变量的定义;接下来需要对View的宽度和高度进行测量;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //计算宽度  字母的宽度取决于画笔的大小
        int textWidth = getTextWidth("A");
        //左间距+有间距+字体宽度
        int width = getPaddingLeft() + getPaddingRight() + textWidth;
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }
    

    高度的话直接通过MeasureSpec.getSize(heightMeasureSpec);可以获取,宽度则需要考虑是否设置了左边距和右边距;测量完成后,调用onDraw()方法进行绘制;

    @Override
    protected void onDraw(Canvas canvas) {
        //每个字体的高度=(屏幕的高度-上边距-下边距)/字母数组的长度
        int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / litterArray.length;
        for (int i = 0; i < litterArray.length; i++) {
            //每个字母的中心位置
            int centerY = i * itemHeight + itemHeight / 2 + getPaddingTop();
            Paint.FontMetricsInt metricsInt = mPaint.getFontMetricsInt();
            int dy = (metricsInt.bottom - metricsInt.top) / 2 - metricsInt.bottom;
            int baseLine = centerY + dy;
    
            int textWidth = getTextWidth(litterArray[i]);
            int x = getWidth() / 2 - textWidth / 2;
            //基线
            //如果当前字母高亮
            if (litterArray[i].equals(mCurrentTouchLetter)) {
                canvas.drawText(litterArray[i], x, baseLine, choicePaint);
            } else {
                canvas.drawText(litterArray[i], x, baseLine, mPaint);
            }
        }
    }
    

    在进行绘制text文本的时候需要注意一个基线,测量、绘制完成后剩下就只有手势触摸了,在onTouchEvent方法中进行手势触摸的处理及回调;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                //计算出当前的字母  获取当前的位置  除以字母的高度,获取位置
                float currentMoveY = event.getY();
                int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / litterArray.length;
                int currentPosition = (int) (currentMoveY / itemHeight);
                if (currentPosition < 0) {
                    currentPosition = 0;
                }
                if (currentPosition > litterArray.length - 1) {
                    currentPosition = litterArray.length - 1;
                }
                String s = litterArray[currentPosition];
                if (mCurrentTouchLetter == null) {
                    mCurrentTouchLetter = "";
                }
                if (mCurrentTouchLetter.equals(s)) {
                    return true;
                }
                mCurrentTouchLetter = s;
                if (mListener != null) {
                    mListener.touch(mCurrentTouchLetter, true);
                }
                //重新绘制
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (mListener != null) {
                    mListener.touch(mCurrentTouchLetter, false);
                }
                break;
            }
        return true;
    }
    

    这样就处理好了,调用即可:

    public class MainActivity extends AppCompatActivity {
        private LitterSideBar sideBar;
        private TextView currentText;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sideBar  = (LitterSideBar) findViewById(R.id.side_bar);
            currentText= (TextView) findViewById(R.id.current_text);
            currentText.setVisibility(View.GONE);
    
            sideBar.setLetterTouchListener(new LitterSideBar.LetterTouchListener() {
                @Override
                public void touch(String letter,boolean isTouch) {
                    if(isTouch){
                        currentText.setVisibility(View.VISIBLE);
                        currentText.setText(""+letter);
                    }else{
                        currentText.setVisibility(View.GONE);
                    }
                }
            });
        }
    }
    

    源码地址:
    https://pan.baidu.com/s/1o9O6h9S

    device-2018-03-04-212230.gif

    下面这个效果和上面那个效果的实现其实大同小异,下面这个只是在触摸滑动字母的时候有一个动画效果,实现如下:

    public class SlideView extends View {
        private String[] mLetters;
        private Paint mPaint;
        private int mChoose;
        private float mDensity;
        private RectF mIsDownRect = new RectF();
        //是否触摸拖动
        private boolean mIsBeingDragger;
        //用于判断动画
        private boolean mStartEndAnim;
        //最小触动距离  在手机启动时就已经注入,每个手机的值会不一样
        private int mTouchSlop;
        private float mHalfWidth, mHalfHeight;
        //每一个字母的高度
        private float mLetterHeight;
        private int mAnimStep;
        private float mY;
        private float mInitDownY;
        private SlideListener listener;
    
        public void setListener(SlideListener listener) {
            this.listener = listener;
        }
    
        public SlideView(Context context) {
            this(context, null);
        }
    
        public SlideView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        private void init(Context context) {
            //初始化画笔
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.GRAY);
            mPaint.setTextAlign(Paint.Align.CENTER);
            //获取字母数组
            mLetters = context.getResources().getStringArray(R.array.letter_list);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            mDensity = getContext().getResources().getDisplayMetrics().density;
            //设置边距
            setPadding(0, dip2px(20), 0, dip2px(20));
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mHalfHeight = h - getPaddingTop() - getPaddingBottom();
            mHalfWidth = w - dip2px(16);
            mLetterHeight = mHalfHeight / mLetters.length;
            //字体大小
            int textSize = (int) (mLetterHeight * 0.7);
            mPaint.setTextSize(textSize);
            mIsDownRect.set(w - dip2px(32), 0, w, h);
        }
    
        private int dip2px(int dippx) {
            return (int) (dippx * mDensity + 0.5f);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < mLetters.length; i++) {
                //通过左下顶点计算
                float letterPosY = mLetterHeight * (i + 1) + getPaddingTop();
                float diff, diffY, diffX;
                //当前字母被选中
                if (mChoose == i && i != 0 && i != mLetters.length - 1) {
                    //被选字母不是第一个且不是最后一个
                    diff = 2.2f;
                    diffX = 0f;
                    diffY = 0f;
                } else {
                    float maxPox = Math.abs(mY - letterPosY) / mHalfHeight * 7;
                    diff = Math.max(1f, 2.2f - maxPox);
                    //没有被选中
                    //是否在触摸点击的范围
                    if (mStartEndAnim && diff != 1f) {
                        diff -= mAnimStep;
                        if (diff < 1) {
                            diff = 1f;
                        }
                    } else if (!mIsBeingDragger) {
                        //没有手指拖动恢复到原理的状态
                        diff = 1f;
                    }
                    diffY = maxPox * 50f * (letterPosY > mY ? -1 : 1);
                    diffX = maxPox * 100;
    
                }
                canvas.save();
                //进行绘制字母
                canvas.scale(diff, diff, mHalfWidth * 1.20f + diffX, letterPosY + diffY);
                if (diff == 1f) {
                    mPaint.setAlpha(225);
                    mPaint.setTypeface(Typeface.DEFAULT);
                } else {
                    int alpa = (int) (225 * (1 - Math.min(0.9f, diff - 1)));
                    if (mChoose == i) {
                        alpa = 225;
                    }
                    mPaint.setAlpha(alpa);
                    mPaint.setTypeface(Typeface.DEFAULT_BOLD);
                }
                canvas.drawText(mLetters[i], mHalfWidth, letterPosY, mPaint);
                canvas.restore();
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int action = MotionEventCompat.getActionMasked(event);
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mIsBeingDragger = false;
                    float initDownY = event.getY();
                    if (!mIsDownRect.contains(event.getX(), event.getY())) {
                        return false;
                    }
                    mInitDownY = initDownY;
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    mStartEndAnim = false;
                    mIsBeingDragger = false;
                    mChoose = -1;
                    mAnimStep = 0;
                    invalidate();
                    return false;
                case MotionEvent.ACTION_MOVE:
                    float y = event.getY();
                    float diff = Math.abs(y - mInitDownY);
                    if (diff > mTouchSlop && !mIsBeingDragger) {
                        mIsBeingDragger = true;
                    }
                    if (mIsBeingDragger) {
                        mY = y;
                        float moveY = y - getPaddingTop();
                        int charcter = (int) (moveY / mHalfHeight * mLetters.length);
                        if (mChoose != charcter) {
                            if (charcter >= 0 && charcter < mLetters.length) {
                                //进行回调
                                mChoose = charcter;
                                String mLetter = mLetters[mChoose];
                                if (listener != null) {
                                    listener.slideListener(mLetter);
                                }
                            }
                        }
                        //进行重绘
                        invalidate();
                    }
                    break;
            }
            return true;
        }
    
        interface SlideListener {
            void slideListener(String letter);
        }
    }
    
    

    调用:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            SlideView slideView = (SlideView) findViewById(R.id.slide_view);
            final TextView tvChoice = (TextView) findViewById(R.id.tv_choice);
            slideView.setListener(new SlideView.SlideListener() {
                @Override
                public void slideListener(String letter) {
                    tvChoice.setText(letter);
                }
            });
        }
    }
    

    源码地址:
    https://pan.baidu.com/s/1nwNSmyX

    相关文章

      网友评论

          本文标题:列表字母侧边指示器效果

          本文链接:https://www.haomeiwen.com/subject/qqbqfftx.html