自定义分段进度条优化

222 阅读2分钟

前言

本篇针对第一篇自定义分段进度条存在的问题进行优化,主要进行两点优化:

一、一半圆角矩形的绘制

上一篇中我们是通过Canvas绘制一个半圆组合一个矩形的方式绘制,现在我们替换成Path的方式绘制更简单。

//绘制渐变色
private void drawGradient(Canvas canvas) {
    if (curStep == 0)
        return;

    Log.d("lz_test", "curStep=" + curStep);
    LinearGradient linearGradient = new LinearGradient(0, 0, barWidth, barHeight, gradientStart, gradientEnd, Shader.TileMode.MIRROR);
    if (curStep == step) {
        paint.reset();
        paint.setAntiAlias(true);//抗锯齿
        paint.setDither(true);//图形抖动
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(1);
        paint.setShader(linearGradient);
        RectF rectF = new RectF(0, 0, barWidth, barHeight);
        canvas.drawRoundRect(rectF, corner, corner, paint);
        return;
    }

    //Path绘制
    paint.reset();
    paint.setAntiAlias(true);//抗锯齿
    paint.setDither(true);//图形抖动
    paint.setStyle(Paint.Style.FILL);
    paint.setStrokeWidth(1);
    paint.setShader(linearGradient);
    RectF ovalRecF = new RectF(0, 0, barHeight, barHeight);
    float rectWidth = curStep * stepWidth + (curStep - 1) * dividerWidth - corner;
    path.reset();
    path.addArc(ovalRecF, 90, 180);//绘制半圆
    path.rLineTo(rectWidth, 0);
    path.rLineTo(0, barHeight);
    path.close();
    canvas.drawPath(path, paint);
}

二、触摸过度绘制问题

我们在监听到ACTION_MOVE和ACTION_CANCE时先计算出触摸区域是第几个段,比对curStep的值如果不相同时我们才重新绘制。

计算触点位于第几段:

private int getTouchStep(float x) {
    //先计算出触点范围属于第几段
    int targetStep = curStep;
    for (int i = 0; i < step; i++) {
        float startX = i * stepWidth + i * dividerWidth;
        float endX = (i == step - 1) ? barWidth : ((i + 1) * stepWidth + (i + 1) * dividerWidth);

        if (x > startX && x <= endX) {
            targetStep = i + 1;
            break;
        }
    }
    return targetStep;
}

比对当前所在的段数curStep:

private void dealViewTouch(float x, boolean complete) {
    //判断步阶是否改变了
    int targetStep = getTouchStep(x);
    boolean stepIsChange = targetStep != curStep;
    if (stepIsChange) {//步阶改变了才去重绘
        curStep = targetStep;
        invalidate();
        if (onDragStepListener != null)
            onDragStepListener.onDragStep(curStep, complete);
    }

}

完整代码:

public class DragStepBar extends View {

    //背景颜色
    private int bgColor = 0xFFDDDDDD;

    //渐变颜色
    private int gradientStart = 0xFF91C1FE;
    private int gradientEnd = 0xFF495FE9;

    //遮罩颜色
    private int dividerColor = 0x80FFFFFF;

    //画笔
    private Paint paint;
    private Path path;

    //高度
    private int barHeight;

    //宽度
    private int barWidth;

    //左右两头圆角
    private float corner;

    //步阶
    private int step = 6;

    //当前步阶
    private int curStep = 6;

    //一段的宽度
    private float stepWidth;

    //分割线的宽度
    private float dividerWidth;

    //是否可以滑动
    private boolean touchEnable = true;
    private OnDragStepListener onDragStepListener;

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

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

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


    private void init(Context context, AttributeSet attrs) {
        DisplayMetrics displayMetrics = getDisplayMetrics();
        barWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 322, displayMetrics);
        barHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 28, displayMetrics);
        dividerWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, displayMetrics);


        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragStepBar);

        //获取进度条背景色
        bgColor = a.getColor(R.styleable.DragStepBar_bar_bg, bgColor);
        //获取渐变色开始颜色
        gradientStart = a.getColor(R.styleable.DragStepBar_bar_color_start, gradientStart);
        //获取渐变色结束颜色
        gradientEnd = a.getColor(R.styleable.DragStepBar_bar_color_end, gradientEnd);
        //分割线颜色
        dividerColor = a.getColor(R.styleable.DragStepBar_bar_divider_color, dividerColor);
        //分割线宽度
        dividerWidth = a.getDimension(R.styleable.DragStepBar_bar_divider_width, dividerWidth);
        //总步数
        step = a.getInt(R.styleable.DragStepBar_bar_step_count, step);
        //当前步数
        curStep = a.getInt(R.styleable.DragStepBar_bar_cur_step, curStep);
        if (curStep > step)
            curStep = step;
        a.recycle();
        paint = new Paint();
        path = new Path();
    }

    private DisplayMetrics getDisplayMetrics() {
        return getResources().getDisplayMetrics();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制底部背景
        drawBackGround(canvas);
        //绘制步阶进度
        drawGradient(canvas);
        //绘制分割线
        drawShade(canvas);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {//宽高都是wrap_content,使用默认值
            setBarMeasure(barWidth, barHeight);
        } else if (widthMode == MeasureSpec.AT_MOST) {
            setBarMeasure(barWidth, heightSize);
        } else if (heightMode == MeasureSpec.AT_MOST) {
            setBarMeasure(widthSize, barHeight);
        } else {
            setBarMeasure(widthSize, heightSize);
        }

    }

    /**
     * 根据测量的宽高计算出单个进度的宽度和圆角的大小
     */
    private void setBarMeasure(int width, int height) {
        barWidth = width;
        barHeight = height;
        stepWidth = (barWidth - (step - 1) * dividerWidth) / step;
        corner = (float) barHeight / 2;
        setMeasuredDimension(width, height);
    }

    //绘制底色
    private void drawBackGround(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);//抗锯齿
        paint.setDither(true);//图形抖动
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(5);
        paint.setColor(bgColor);
        RectF rectF = new RectF(0, 0, barWidth, barHeight);
        canvas.drawRoundRect(rectF, corner, corner, paint);
    }

    //绘制渐变色
    private void drawGradient(Canvas canvas) {
        if (curStep == 0)
            return;

        Log.d("lz_test", "curStep=" + curStep);
        LinearGradient linearGradient = new LinearGradient(0, 0, barWidth, barHeight, gradientStart, gradientEnd, Shader.TileMode.MIRROR);
        if (curStep == step) {
            paint.reset();
            paint.setAntiAlias(true);//抗锯齿
            paint.setDither(true);//图形抖动
            paint.setStyle(Paint.Style.FILL);
            paint.setStrokeWidth(1);
            paint.setShader(linearGradient);
            RectF rectF = new RectF(0, 0, barWidth, barHeight);
            canvas.drawRoundRect(rectF, corner, corner, paint);
            return;
        }

        //Path绘制
        paint.reset();
        paint.setAntiAlias(true);//抗锯齿
        paint.setDither(true);//图形抖动
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(1);
        paint.setShader(linearGradient);
        RectF ovalRecF = new RectF(0, 0, barHeight, barHeight);
        float rectWidth = curStep * stepWidth + (curStep - 1) * dividerWidth - corner;
        path.reset();
        path.addArc(ovalRecF, 90, 180);
        path.rLineTo(rectWidth, 0);
        path.rLineTo(0, barHeight);
        path.close();
        canvas.drawPath(path, paint);
    }

    //绘制分割线
    private void drawShade(Canvas canvas) {
        paint.reset();
        paint.setColor(dividerColor);
        paint.setAntiAlias(true);//抗锯齿
        paint.setDither(true);//图形抖动
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(1);
        for (int i = 1; i < step; i++) {
            float left = stepWidth * i + dividerWidth * (i - 1);
            RectF rectF = new RectF(left, 0, left + dividerWidth, barHeight);
            canvas.drawRect(rectF, paint);
        }
    }


    //设置是否可点击
    public void setTouchEnable(boolean touchEnable) {
        this.touchEnable = touchEnable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!touchEnable) {
            return false;
        }
        //获取触摸坐标
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                dealViewTouch(event.getX(), false);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                dealViewTouch(event.getX(), true);
                break;
        }
        getParent().requestDisallowInterceptTouchEvent(true);
        return true;

    }

    private void dealViewTouch(float x, boolean complete) {
        //判断步阶是否改变了
        int targetStep = getTouchStep(x);
        boolean stepIsChange = targetStep != curStep;
        if (stepIsChange) {//步阶改变了才去重绘
            curStep = targetStep;
            invalidate();
            if (onDragStepListener != null)
                onDragStepListener.onDragStep(curStep, complete);
        }

    }

    //获取触点所属第几段
    private int getTouchStep(float x) {
        //先计算出触点范围属于第几段
        int targetStep = curStep;
        for (int i = 0; i < step; i++) {
            float startX = i * stepWidth + i * dividerWidth;
            float endX = (i == step - 1) ? barWidth : ((i + 1) * stepWidth + (i + 1) * dividerWidth);

            if (x > startX && x <= endX) {
                targetStep = i + 1;
                break;
            }
        }
        return targetStep;
    }


    //获取当前步阶
    public int getCurStep() {
        return curStep;
    }

    public void setOnDragStepListener(OnDragStepListener onDragStepListener) {
        this.onDragStepListener = onDragStepListener;
    }

    ///设置默认步阶
    public void setDefaultStep(int step) {
        curStep = step;
        invalidate();
    }

    public interface OnDragStepListener {
        void onDragStep(int step, boolean moveCancel);
    }
}