上一篇,介绍了如何做一个横向的进度条,非常简单。
今天,我们趁热打铁,做一个常见的环形进度条。诺,长下面这个样子↓
一、分析
按照惯例,先分析元素。这里,跟画横向进度条的不同是,横向进度条是画两个圆角矩形,这里,我们需要花两个圆(进度条是根据实时进度值画出的圆弧,最终补全为360°的圆)。
1、背景环颜色
2、背景环&进度条相同的画笔width
3、背景环设置为空心
4、进度条颜色
5、文字颜色、字体大小
二、实现
因为跟横向进度条有许多相似的地方,不同之处,也就在onDraw的实现上,此处,就在前一篇文章的横向进度条的基础上进行完善。之后,使用这一个工具类,你只需稍作修改就可以实现横向、环形进度条,灵活使用很Happy。
这里,在attr中,多加入一个变量,cp_orientation,来区分进度条的形式,是横向的还是环形的。
<declare-styleable name="CustomProgressView">
<!--百分比文字大小-->
<attr name="cp_percent_textsize" format="dimension"/>
<!--百分比文字颜色-->
<attr name="cp_percent_textcolor" format="color|integer"/>
<!--进度条背景颜色-->
<attr name="cp_background_color" format="color|integer"/>
<!--进度条背景是否空心-->
<attr name="cp_background_is_stroke" format="boolean"/>
<!--进度条颜色-->
<attr name="cp_progress_color" format="color|integer"/>
<!--进度条圆角值-->
<attr name="cp_rect_round" format="dimension"/>
<!--进度条形式-->
<attr name="cp_orientation">
<enum name="horizontal" value="0"/>
<enum name="circle" value="1"/>
</attr>
</declare-styleable>
根据不同的取值,来画不同的进度条
@Override
protected void onDraw(Canvas canvas) {
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
if (cp_orientation == HORIZONTAL) {
drawHorProgress(mPaint, canvas);
} else if (cp_orientation == CIRCLE) {
drawCircleProgress(mPaint, canvas);
}
}
//画环形进度条
private void drawCircleProgress(Paint paint, Canvas canvas) {
//画背景环
paint.setColor(cp_background_color);
if (cp_background_is_stroke) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(paint_width);
} else {
paint.setStyle(Paint.Style.FILL);
}
int radius = (int) (Math.min(getWidth() / 2, getHeight() / 2) - paint.getStrokeWidth() / 2);
canvas.drawCircle(mCenterX, mCenterY, radius, paint);
//画进度环
paint.setColor(cp_progress_color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(paint_width);
RectF rectF = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);
canvas.drawArc(rectF, -90, 360 * progressCurrent / progressMax, false, paint);
//画文字
paint.setColor(cp_percent_textcolor);
paint.setTextSize(cp_percent_textsize);
paint.setStyle(Paint.Style.FILL);
String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
Rect rect = new Rect();
paint.getTextBounds(value_str, 0, value_str.length(), rect);
float textWidth = rect.width();
float textHeight = rect.height();
if (textWidth >= radius * 2) {
textWidth = radius * 2;
}
Paint.FontMetrics metrics = paint.getFontMetrics();
float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);
}
这里,在计算半径时,选择getWidth和getHeight的最小值,然后减去画笔宽度的一半。这样,使这个圆被包裹在view的布局之中。圆的大小可以根据布局宽高的变化进行动态调整。
进度环,画的是圆弧;进度背景画的是圆,两者有区别,如果你不熟悉画圆弧,可以去熟悉一下这方面的知识。
最后,注意一下,文字的大小不要超出圆的直径。
三、布局
<net.feelingtech.example_work.custom.ownprogress.CustomProgressView
android:id="@+id/cpv_circle"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="20dp" />
四、跑起来
mCpv_circle = view.findViewById(R.id.cpv_circle);
mCpv_circle.setCp_orientation(1);
mCpv_circle.setCp_background_is_stroke(true);
mCpv_circle.setPaint_width(DensityUtil.dip2px(getContext(),10));
mCpv_circle.setCp_background_color(Color.parseColor("#A2CD5A"));
mCpv_circle.setProgressMax(100);
mCpv_circle.setCp_progress_color(Color.RED);
mCpv_circle.setCp_percent_textsize(DensityUtil.dip2px(getContext(), 16));
mCpv_circle.setCp_percent_textcolor(Color.RED);
view.findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (progressCurrent != 0) {
progressCurrent = 0;
return;
}
mRunnable = new Runnable() {
@Override
public void run() {
progressCurrent += 1;
mCustomProgressView.setProgressCurrent(progressCurrent);
mCpv_circle.setProgressCurrent(progressCurrent);
mHandler.postDelayed(mRunnable, 5);
}
};
mHandler.postDelayed(mRunnable, 1);
}
});
五、完整代码
public class CustomProgressView extends View {
private int cp_percent_textsize = 18;//百分比字体大小
private int cp_percent_textcolor = 0xff009ACD;
private int cp_background_color = 0xff636363;
private int cp_progress_color = 0xff00C5CD;
private boolean cp_background_is_stroke = true;
private int cp_rect_round = 5;
private int HORIZONTAL = 0;
private int CIRCLE = 1;
private int cp_orientation = HORIZONTAL;
private Paint mPaint;
private int mCenterX;
private int mCenterY;
private int progressCurrent = 0;
private int progressMax = 100;
private int paint_width = 10;
public CustomProgressView(Context context) {
this(context, null);
}
public CustomProgressView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(R.styleable.CustomProgressView);
cp_percent_textsize = (int) typedArray.getDimension(R.styleable.CustomProgressView_cp_percent_textsize, cp_percent_textsize);
cp_percent_textcolor = typedArray.getColor(R.styleable.CustomProgressView_cp_percent_textcolor, cp_percent_textcolor);
cp_background_color = typedArray.getColor(R.styleable.CustomProgressView_cp_background_color, cp_background_color);
cp_progress_color = typedArray.getColor(R.styleable.CustomProgressView_cp_progress_color, cp_progress_color);
cp_background_is_stroke = typedArray.getBoolean(R.styleable.CustomProgressView_cp_background_is_stroke, cp_background_is_stroke);
cp_rect_round = (int) typedArray.getDimension(R.styleable.CustomProgressView_cp_rect_round, cp_rect_round);
cp_orientation = typedArray.getInteger(R.styleable.CustomProgressView_cp_orientation, cp_orientation);
typedArray.recycle();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
if (cp_orientation == HORIZONTAL) {
drawHorProgress(mPaint, canvas);
} else if (cp_orientation == CIRCLE) {
drawCircleProgress(mPaint, canvas);
}
}
//画环形进度条
private void drawCircleProgress(Paint paint, Canvas canvas) {
//画背景环
paint.setColor(cp_background_color);
if (cp_background_is_stroke) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(paint_width);
} else {
paint.setStyle(Paint.Style.FILL);
}
int radius = (int) (Math.min(getWidth() / 2, getHeight() / 2) - paint.getStrokeWidth() / 2);
canvas.drawCircle(mCenterX, mCenterY, radius, paint);
//画进度环
paint.setColor(cp_progress_color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(paint_width);
RectF rectF = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);
canvas.drawArc(rectF, -90, 360 * progressCurrent / progressMax, false, paint);
//画文字
paint.setColor(cp_percent_textcolor);
paint.setTextSize(cp_percent_textsize);
paint.setStyle(Paint.Style.FILL);
String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
Rect rect = new Rect();
paint.getTextBounds(value_str, 0, value_str.length(), rect);
float textWidth = rect.width();
float textHeight = rect.height();
if (textWidth >= radius * 2) {
textWidth = radius * 2;
}
Paint.FontMetrics metrics = paint.getFontMetrics();
float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);
}
//画线性进度条
private void drawHorProgress(Paint paint, Canvas canvas) {
//画背景
paint.setColor(cp_background_color);
if (cp_background_is_stroke) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
} else {
paint.setStyle(Paint.Style.FILL);
}
canvas.drawRoundRect(new RectF(mCenterX - getWidth() / 2, mCenterY - getHeight() / 2,
mCenterX + getWidth() / 2, mCenterY + getHeight() / 2), cp_rect_round, cp_rect_round, paint);
//画进度条
paint.setColor(cp_progress_color);
paint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(new RectF(mCenterX - getWidth() / 2, mCenterY - getHeight() / 2,
(int) (progressCurrent * getWidth() / progressMax), mCenterY + getHeight() / 2), cp_rect_round, cp_rect_round, paint);
//画文字
paint.setColor(cp_percent_textcolor);
paint.setTextSize(cp_percent_textsize);
paint.setStyle(Paint.Style.FILL);
String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
Rect rect = new Rect();
paint.getTextBounds(value_str, 0, value_str.length(), rect);
float textWidth = rect.width();
float textHeight = rect.height();
if (textWidth >= getWidth()) {
textWidth = getWidth();
}
Paint.FontMetrics metrics = paint.getFontMetrics();
float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);
}
public int getCp_percent_textsize() {
return cp_percent_textsize;
}
public void setCp_percent_textsize(int cp_percent_textsize) {
this.cp_percent_textsize = cp_percent_textsize;
}
public int getCp_percent_textcolor() {
return cp_percent_textcolor;
}
public void setCp_percent_textcolor(int cp_percent_textcolor) {
this.cp_percent_textcolor = cp_percent_textcolor;
}
public int getCp_background_color() {
return cp_background_color;
}
public void setCp_background_color(int cp_background_color) {
this.cp_background_color = cp_background_color;
}
public int getCp_progress_color() {
return cp_progress_color;
}
public void setCp_progress_color(int cp_progress_color) {
this.cp_progress_color = cp_progress_color;
}
public boolean isCp_background_is_stroke() {
return cp_background_is_stroke;
}
public void setCp_background_is_stroke(boolean cp_background_is_stroke) {
this.cp_background_is_stroke = cp_background_is_stroke;
}
public int getCp_rect_round() {
return cp_rect_round;
}
public void setCp_rect_round(int cp_rect_round) {
this.cp_rect_round = cp_rect_round;
}
public int getProgressCurrent() {
return progressCurrent;
}
public void setProgressCurrent(int progressCurrent) {
if (progressCurrent > progressMax) {
this.progressCurrent = progressMax;
} else {
this.progressCurrent = progressCurrent;
}
postInvalidate();
}
public int getProgressMax() {
return progressMax;
}
public void setProgressMax(int progressMax) {
this.progressMax = progressMax;
}
public int getCp_orientation() {
return cp_orientation;
}
public void setCp_orientation(int cp_orientation) {
this.cp_orientation = cp_orientation;
}
public int getPaint_width() {
return paint_width;
}
public void setPaint_width(int paint_width) {
this.paint_width = paint_width;
}
}