列表字母侧边指示器效果

2018-03-04  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读