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);
}
}
复制代码
完成。