天天看點

自定義橫向帶刻度progressbar

項目需要,需要自定義帶漸變色的進度條,效果如下:

自定義橫向帶刻度progressbar

1,思路

  • 首先繪制 roundRect 帶半圓的矩形背景
  • 繪制一個 可變的rect矩形 ,讓rect的寬度不斷的變大來實作對下面roundRect覆寫
  • 通過PorterDuffXfermode的PorterDuff.Mode.SRC_IN來取可變rect跟背景rect相交的部分
  • 記得取消硬體加速

2,用到的主要知識點

  • LinearGradient 漸變色
  • PorterDuffXfermode 去交集部分渲染

3,開始繪制

  • 1,初始化,onsizechange ,onMeasure省略…
  • 2,畫背景跟邊
canvas.drawRoundRect(mBgRect, mViewHeight / 2, mViewHeight / 2, mBgPaint);
  canvas.drawRoundRect(mBgRect, mViewHeight / 2, mViewHeight / 2, mStrokePaint);

           
  • 3,初始化可變的rect矩形跟漸變色的shader
//可變矩形mProgressWidth
  RectF progressRect = new RectF(mStrokeWidth / 2, mStrokeWidth / 2, mProgressWidth - mStrokeWidth / 2, mViewHeight - mStrokeWidth / 2);
	//漸變色的起始x,y;終點x,y;漸變色的開始,終止顔色,模式
 Shader mShader = new LinearGradient(mStrokeWidth / 2, mStrokeWidth / 2, mProgressWidth, mHeight - mStrokeWidth / 2, Color.parseColor("#27a6ff"), Color.parseColor("#099aff"), Shader.TileMode.REPEAT);
           
  • 4,繪制這個遮罩的rect
//遮罩
        mProgressPaint.setXfermode(mXfermode);
        mProgressPaint.setShader(mShader);
        canvas.drawRect(progressRect, mProgressPaint);
	mProgressPaint.setXfermode(null);
           
  • 5,繪制刻度
//刻度
        for (int i = 0; i < mTickNums - 1; i++) {
            canvas.drawLine((i + 1) * mMackTickWidth, mStrokeWidth / 2, (i + 1) * mMackTickWidth, mViewHeight - mStrokeWidth / 2, mMackTickPaint);
        }  
           
  • 6,提供對外方法
/**
     * progress取值範圍[0-1]
     */
    public void setProgress(float progress) {
        if (progress < 0) {
            progress = 0;
        } else if (progress > 1) {
            progress = 1;
        }

        int delta = (int) (progress / 0.1);
        delta = delta == 0 ? 1 : delta;

        mAnimator = ValueAnimator.ofFloat(0, progress).setDuration(delta * DURATION);

        mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimatorValue = (float) animation.getAnimatedValue();
				//不斷的改變可變矩形的寬度
                mProgressWidth = (int) (mAnimatorValue * mViewWidth);
                invalidate();
            }
        };
        mAnimator.addUpdateListener(mUpdateListener);
        mAnimator.start();
    }

           

這裡是一個大概的實作,完整代碼。

public class TickProgress extends View {
    private static final long DURATION = 200;
    private int mViewWidth;
    private int mViewHeight;

    private int mStrokeWidth;

    private int mProgressWidth;

    private Paint mBgPaint;
    private Paint mProgressPaint;
    private Paint mMackTickPaint;


    private int mStrokeColor = Color.parseColor("#a3daff");

    private ValueAnimator.AnimatorUpdateListener mUpdateListener;
    private ValueAnimator mAnimator;
    // 動畫數值(用于控制動畫狀态,因為同一時間内隻允許有一種狀态出現,具體數值處理取決于目前狀态)
    private float mAnimatorValue;


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

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

    private Paint mStrokePaint;

    private int mHeight;

    public TickProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mHeight = DisplayUtil.dip2px(context, 10);
        mStrokeWidth = DisplayUtil.dip2px(context, 1);
        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBgPaint.setStyle(Paint.Style.FILL);
        mBgPaint.setStrokeWidth(mHeight);
        mBgPaint.setColor(Color.parseColor("#e6f6ff"));

        mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mStrokePaint.setStrokeWidth(mStrokeWidth);
        mStrokePaint.setColor(mStrokeColor);

        mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mProgressPaint.setStrokeWidth(mHeight);
        mProgressPaint.setStyle(Paint.Style.FILL);

        mMackTickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mMackTickPaint.setStyle(Paint.Style.STROKE);
        mMackTickPaint.setStrokeWidth(mStrokeWidth);
        mMackTickPaint.setColor(mStrokeColor);

        setLayerType(LAYER_TYPE_HARDWARE, null);
    }

    private int mMackTickWidth;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewHeight = h;
        mViewWidth = w;
        mMackTickWidth = w / 10;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(widthMeasureSpec, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        RectF rectF2 = new RectF(mStrokeWidth, mStrokeWidth, mViewWidth - mStrokeWidth, mHeight - mStrokeWidth);

        canvas.drawRoundRect(rectF2, mHeight / 2, mHeight / 2, mBgPaint);
        canvas.drawRoundRect(rectF2, mHeight / 2, mHeight / 2, mStrokePaint);


        RectF rectF1 = new RectF(mStrokeWidth / 2, mStrokeWidth / 2, mProgressWidth - mStrokeWidth / 2, mViewHeight - mStrokeWidth / 2);
        Path path = new Path();

        Path dstPath = new Path();
        PathMeasure pathMeasure = new PathMeasure();
        path.moveTo(0, 0);
        path.lineTo(mViewWidth, 0);
        pathMeasure.setPath(path, false);
        pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, dstPath, true);
        //漸變
        Shader mShader = new LinearGradient(mStrokeWidth / 2, mStrokeWidth / 2, mProgressWidth, mHeight - mStrokeWidth / 2, Color.parseColor("#27a6ff"), Color.parseColor("#099aff"), Shader.TileMode.REPEAT);
        //遮罩
        mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mProgressPaint.setShader(mShader);
        canvas.drawRect(rectF1, mProgressPaint);
        mProgressPaint.setXfermode(null);
        //刻度
        for (int i = 0; i < 9; i++) {
            canvas.drawLine((i + 1) * mMackTickWidth, mStrokeWidth / 2, (i + 1) * mMackTickWidth, mViewHeight - mStrokeWidth / 2, mMackTickPaint);
        }
    }

    /**
     * 0--1
     */
    public void setProgress(float progress) {
        if (progress < 0) {
            progress = 0;
        } else if (progress > 1) {
            progress = 1;
        }

        int delta = (int) (progress /0.1);
        LogUtils.d("delta:"+delta);
        delta = delta == 0 ? 1 : delta;

        mProgressWidth = (int) (progress * mViewWidth);
        mAnimator = ValueAnimator.ofFloat(0, progress).setDuration(delta * DURATION);

        mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimatorValue = (float) animation.getAnimatedValue();
                mProgressWidth = (int) (mAnimatorValue * mViewWidth);
                invalidate();
            }
        };
        mAnimator.addUpdateListener(mUpdateListener);
        mAnimator.start();
    }
}