首先得写一个类RoundProgress继承View,重写其中onMeasure()和onDraw()方法。在onMeasure()里获取当前画布的宽度。
在onDraw()方法里主要绘制如下三部分:
- 绘制圆环
- 绘制圆弧
- 绘制文本
相关属性如下:
/**
* 圆环的颜色
*/
private int ringColor;
// 圆环进度的颜色
private int ringProgressColor;
// 圆环的宽度
private int ringWidth;
// 字体大小
private int textSize;
// 字体颜色
private int textColor ;
// 画笔
private Paint paint;
// 得到控件宽度
private int width;
// 最大进度
private int max;
// 当前进度
private int progress;
在绘制之前我们应该先明白相关坐标,如下图:
- 圆环的圆心坐标: x , y都是画布宽度的一半width/2,width/2,半径是画布宽度的一半 减去圆环宽度的一半radius = width/2 - roundWidth/2;
绘制圆环的代码如下:
// 圆心坐标和半径
float circleX = width / 2;
float circleY = width / 2;
float radius = width / 2 - ringWidth / 2;
// 1、 绘制圆环
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(ringWidth);
paint.setColor(ringColor);
canvas.drawCircle(circleX, circleY, radius, paint);
- 绘制圆弧的边界应该也能看图得出来,代码如下:
// 2、 绘制圆弧
RectF oval = new RectF(ringWidth / 2, ringWidth / 2, width - ringWidth / 2, width - ringWidth / 2);
paint.setColor(ringProgressColor);
canvas.drawArc(oval, 0, progress * 360 / max, false, paint);
- 绘制文本,首先根据指定文本得到包裹文本的矩形大小,然后在指定位置开始绘制文本。这里要注意的是:文本是从左下角开始绘制的,位置见上图中的textX,textY;还有一定要将绘制圆环时的画笔宽度设置为0.具体代码:
// 3、绘制文本
String text = progress * 100 / max + "%";
paint.setColor(textColor);
paint.setTextSize(textSize);
// 注意此处一定要重新设置宽度为0
paint.setStrokeWidth(0);
// 得到指定文本的边界矩形大小
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
canvas.drawText(text, width / 2 - bounds.width() / 2, width / 2 + bounds.height() / 2, paint);
为了更方便的使用这个RoundProgress控件,我们应该自定义相关属性attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundProgress">
<attr name="ringColor" format="color"></attr>
<attr name="ringProgressColor" format="color"></attr>
<attr name="ringWidth" format="dimension"></attr>
<attr name="textSize" format="dimension"></attr>
<attr name="textColor" format="color"></attr>
<attr name="max" format="integer"></attr>
<attr name="progress" format="integer"></attr>
</declare-styleable>
</resources>
在构造方法中初始化画笔并得到布局文件中的相关属性。这里顺便提一下三个构造方法的作用,第一个用于java代码创建对象,第二个用于布局文件加载该View,必须有,不然会崩溃。
/**
* 此构造方法用于java代码创建对象
* @param context
*/
public RoundProgress(Context context) {
this(context, null);
}
/**
* 此构造方法用于布局文件加载该View,必须有,不然会崩溃
* @param context
* @param attrs
*/
public RoundProgress(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
// 设置抗锯齿
paint.setAntiAlias(true);
// 得到所有自定义属性的数组
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgress);
ringColor = typedArray.getColor(R.styleable.RoundProgress_ringColor,Color.GRAY);
ringProgressColor = typedArray.getColor(R.styleable.RoundProgress_ringProgressColor, Color.RED);
ringWidth = (int) typedArray.getDimension(R.styleable.RoundProgress_ringWidth,UIUtils.dip2px(10));
textSize = (int) typedArray.getDimension(R.styleable.RoundProgress_textSize,UIUtils.dip2px(20));
textColor = typedArray.getColor(R.styleable.RoundProgress_textColor,Color.RED);
max = typedArray.getInteger(R.styleable.RoundProgress_max,100);
progress = typedArray.getInteger(R.styleable.RoundProgress_progress,60);
// 不要忘记回收
typedArray.recycle();
}
在布局文件中使用这个自定义View.
<com.example.txy.example.view.RoundProgress
android:id="@+id/rp_home"
app:ringColor="@android:color/darker_gray"
app:ringProgressColor="@android:color/holo_red_dark"
app:ringWidth="10dp"
app:textSize="20sp"
app:textColor="@color/text_progress"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="120dp"
android:layout_height="120dp" />
使进度条动态加载显示的代码:
totalProgress = 90;
// 让当前的进度条动态的加载显示
new Thread(new Runnable() {
@Override
public void run() {
rp_home.setMax(100);
rp_home.setProgress(0);
for(int i = 0; i < totalProgress; i++) {
rp_home.setProgress(rp_home.getProgress() + 1);
SystemClock.sleep(30);
// 强制重绘 Use this to invalidate the View from a non-UI thread
rp_home.postInvalidate();
}
}
}).start();
希望对大家有帮助