Android自定義圓形進度條

WJ__DGQN發表於2019-01-18

Android自定義圓形進度條

github地址:https://github.com/opq1289/CircleProgressView

效果圖:
無動畫:

在這裡插入圖片描述

有動畫:

整圓:

在這裡插入圖片描述
切割圓:
在這裡插入圖片描述

具體步驟:

1. 繪製最基礎的兩個圓

定義兩個畫筆:

//進度條畫筆
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeWidth(30);
mProgressPaint.setColor(Color.parseColor("#d81b60"));
//背景圓畫筆
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeWidth(30);
mBackgroundPaint.setColor(Color.parseColor("#f1f1f1"));
複製程式碼

畫圓:

mRectf = new RectF(0, 0, 200, 200);
canvas.drawCircle(100, 100, 100, mBackgroundPaint);
canvas.drawArc(mRectf, 0, 120, false, mProgressPaint);
複製程式碼

在這裡插入圖片描述

邊緣被切割,增加引數進度條寬度,優化邊緣:

private void initPaint() {
    //進度條畫筆
    mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mProgressPaint.setStyle(Paint.Style.STROKE);
    mProgressPaint.setStrokeWidth(mProgressWidth);
    mProgressPaint.setColor(Color.parseColor("#d81b60"));
    //背景圓畫筆
    mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBackgroundPaint.setStyle(Paint.Style.STROKE);
    mBackgroundPaint.setStrokeWidth(mProgressWidth);
    mBackgroundPaint.setColor(Color.parseColor("#f1f1f1"));
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mRectf = new RectF(mProgressWidth / 2, mProgressWidth / 2, 200 - mProgressWidth / 2, 200 - mProgressWidth / 2);
    canvas.drawCircle(100, 100, 100 - mProgressWidth / 2, mBackgroundPaint);
    canvas.drawArc(mRectf, 0, 120, false, mProgressPaint);
}
複製程式碼

在這裡插入圖片描述

2. 計算尺寸

main_activity.xml中的佈局是:

android:layout_width="100dp"
android:layout_height="100dp"
複製程式碼

顯示佈局邊界後發現view的寬高是螢幕寬高:

在這裡插入圖片描述

重新設定view的尺寸:

//設定預設最小尺寸
private int mDefaultWidth = CommonUtil.dp2px(getContext(), 10);
複製程式碼
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = measureWidth(widthMeasureSpec);
    int height = measureHeight(heightMeasureSpec);
    mViewWidth = Math.min(width, height);
    setMeasuredDimension(mViewWidth, mViewWidth);
}
複製程式碼
private int measureWidth(int widthMeasureSpec) {
    int width;
    int size = MeasureSpec.getSize(widthMeasureSpec);
    int mode = MeasureSpec.getMode(widthMeasureSpec);
    switch (mode) {
        case MeasureSpec.EXACTLY:
            width = size < mProgressWidth ? mProgressWidth : size;
            break;
        case MeasureSpec.AT_MOST:
            width = mDefaultWidth * 2;
            break;
        default:
            width = CommonUtil.getScreenWidthInPx(getContext());
            break;
    }
    return width;
}

private int measureHeight(int heightMeasureSpec) {
    int height;
    int size = MeasureSpec.getSize(heightMeasureSpec);
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    switch (mode) {
        case MeasureSpec.EXACTLY:
            height = size < mProgressWidth ? mProgressWidth : size;
            break;
        case MeasureSpec.AT_MOST:
            height = mDefaultWidth * 2;
            break;
        default:
            height = CommonUtil.getScreenHeightInPx(getContext());
            break;
    }
    return height;
}
複製程式碼

然後修改繪製:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mRectf = new RectF(mProgressWidth / 2, mProgressWidth / 2, mViewWidth - mProgressWidth / 2, mViewWidth - mProgressWidth / 2);
    canvas.drawCircle(mViewWidth / 2, mViewWidth / 2, mViewWidth / 2 - mProgressWidth / 2, mBackgroundPaint);
    canvas.drawArc(mRectf, 0, 120, false, mProgressPaint);
}
複製程式碼

在這裡插入圖片描述

至此,靜態進度條就畫好了。

將引數設定為動態,通過方法和屬性設定。

<declare-styleable name="CircleProgressView">
    <attr name="progressWidth" format="dimension"/>
    <attr name="progressColor" format="reference"/>
    <attr name="backgroundColor" format="reference"/>
    <attr name="startAngle" format="integer"/>
</declare-styleable>

複製程式碼
public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initPaint();
    TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyleAttr, 0);
    mProgressWidth = (int) typedArray.getDimension(R.styleable.CircleProgressView_progressWidth, mDefaultWidth);
    mProgressColor = (int) typedArray.getDimension(R.styleable.CircleProgressView_progressColor, ContextCompat.getColor(getContext(), R.color.colorAccent));
    mStartAngle = typedArray.getInt(R.styleable.CircleProgressView_startAngle, 0);
    mEndAngle = typedArray.getInt(R.styleable.CircleProgressView_startAngle, 360);
    mBackgroundColor = (int) typedArray.getDimension(R.styleable.CircleProgressView_backgroundColor, ContextCompat.getColor(getContext(), R.color.grey_f1));
    typedArray.recycle();

    mProgressPaint.setStrokeWidth(mProgressWidth);
    mProgressPaint.setColor(mProgressColor);

    mBackgroundPaint.setStrokeWidth(mProgressWidth);
    mBackgroundPaint.setColor(mBackgroundColor);
}

複製程式碼

3. 新增動畫

public void setProgress(float progress) {
    mValueAnimator = ValueAnimator.ofFloat(progress);
    mValueAnimator.setDuration(1000);
    mValueAnimator.setInterpolator(new LinearInterpolator());
    mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mProgress = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    mValueAnimator.start();
}

複製程式碼

增加進度監聽:

public interface OnProgressChangedListener {
    void onProgressChanged(float currentProgress);
}

複製程式碼
public void setOnProgressChangedListener(OnProgressChangedListener listener) {
    mListener = listener;
}

複製程式碼

在佈局中增加文字,設定居中:

在這裡插入圖片描述

4. 優化程式碼

增加靜態和動畫的區分:

/**
* @param progress      進度
* @param showAnimation 是否展示動畫
*/
public void setProgress(float progress, boolean showAnimation) {
    mShowAnimation = showAnimation;
    if (mValueAnimator != null && mValueAnimator.isRunning()) {
        mValueAnimator.cancel();
    }
    if (mShowAnimation) {
        mValueAnimator = ValueAnimator.ofFloat(progress);
        mValueAnimator.setDuration(mDuration);
        mValueAnimator.setInterpolator(new LinearInterpolator());
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mProgress = (float) animation.getAnimatedValue();
                if (mListener != null) {
                    mListener.onProgressChanged(mProgress);
                }
                invalidate();
            }
        });
        mValueAnimator.start();
    } else {
        mProgress = progress;
        invalidate();
    }
}

複製程式碼

設定畫筆型別,增加圓頭畫筆:

public void setCap(Paint.Cap cap) {
    mProgressPaint.setStrokeCap(cap);
    mBackgroundPaint.setStrokeCap(cap);
}

複製程式碼

在這裡插入圖片描述

5. 增加切割圓型別

增加進度條型別mProgressType:

/**
 * 整圓進度條
 */
public static final int TYPE_CIRCLE = 0;
/**
 * 切割圓進度條
 */
public static final int TYPE_CLIP = 1;

複製程式碼

切割圓從開始角度到結束角度之間,總進度為100。所以這種情況下:總進度=終止角度 - 起始角度

設定進度的方法修改:

public void setProgress(float progress, boolean showAnimation) {
    mShowAnimation = showAnimation;
    if (mProgressType == TYPE_CLIP) {
        progress = (int) ((mEndAngle - mStartAngle) * 100 / 360.0f);
        mTotalProgress = progress;
    } else {
        mTotalProgress = 100;
    }
    if (mValueAnimator != null && mValueAnimator.isRunning()) {
        mValueAnimator.cancel();
    }
    if (mShowAnimation) {
        mValueAnimator = ValueAnimator.ofFloat(progress);
        mValueAnimator.setDuration(mDuration);
        mValueAnimator.setInterpolator(new LinearInterpolator());
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mProgress = (float) animation.getAnimatedValue();
                if (mListener != null) {
                    mListener.onProgressChanged(mProgress * 100 / mTotalProgress);
                }
                invalidate();
            }
        });
        mValueAnimator.start();
    } else {
        mProgress = progress;
        invalidate();
    }
}

複製程式碼

OnDraw修改,區分切割圓和整圓:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mRectf = new RectF(mProgressWidth / 2, mProgressWidth / 2, mViewWidth - mProgressWidth / 2, mViewWidth - mProgressWidth / 2);
    if (mProgressType == TYPE_CIRCLE) {
        canvas.drawCircle(mViewWidth / 2, mViewWidth / 2, mViewWidth / 2 - mProgressWidth / 2, mBackgroundPaint);
        canvas.drawArc(mRectf, mStartAngle, mProgress * 360 / 100, false, mProgressPaint);
    } else if (mProgressType == TYPE_CLIP) {
        canvas.drawArc(mRectf, mStartAngle, mEndAngle - mStartAngle, false, mBackgroundPaint);
        canvas.drawArc(mRectf, mStartAngle, mProgress * 360 / 100, false, mProgressPaint);
    }
}

複製程式碼

完成。

github地址:https://github.com/opq1289/CircleProgressView

相關文章