I found that there are custom controls similar to this in many applications, and I chose a control myself.
1. First customize the attributes to control the band and color of the arc, the middle color and font size, etc.
Create attrs.xml file in the values folder under the res directory.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ArcStatisticsView">
<attr name="OuterLayerColor" format="color" />
<attr name="InnerLayerColor" format="color" />
<attr name="ArcSize" format="dimension" />
<attr name="StatisticsTextSize" format="dimension" />
<attr name="StatisticsTextColor" format="color" />
</declare-styleable>
</resources>
2. Get attributes in the custom View:
//获取自定义的属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcStatisticsView);
OuterLayerColor = typedArray.getColor(R.styleable.ArcStatisticsView_OuterLayerColor, Color.RED);
InnerLayerColor = typedArray.getColor(R.styleable.ArcStatisticsView_InnerLayerColor, Color.GREEN);
ArcSize = (int) typedArray.getDimension(R.styleable.ArcStatisticsView_ArcSize, ArcSize);
StatisticsTextSize = (int) typedArray.getDimension(R.styleable.ArcStatisticsView_StatisticsTextSize, StatisticsTextSize);
StatisticsTextColor = typedArray.getColor(R.styleable.ArcStatisticsView_StatisticsTextColor, Color.GREEN);
typedArray.recycle();
3. Measure the size of the control, generally this kind of control is square.
//测量大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取设置的宽和高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//哪个值小就用哪个值正方形
setMeasuredDimension(widthSize > heightSize ? heightSize : widthSize, widthSize > heightSize ? heightSize : widthSize);
}
4. Drawing:
4.1 Draw the outer circle arc.
private void drawOuterLayer(Canvas canvas) {
//设置画笔的属性
paint.setAntiAlias(true);
//设置画笔颜色
paint.setColor(OuterLayerColor);
//设置画笔大小
paint.setStrokeWidth(ArcSize);
//设置画笔模式(描边模式)
paint.setStyle(Paint.Style.STROKE);
//设置为圆头(如果不设置的话就会是直角边)
paint.setStrokeCap(Paint.Cap.ROUND);
//第四个参数控制是否和圆心连接起来
//创建一个矩阵 圆弧绘制在矩阵中(因为绘制的线条有宽度所以需要减去不然会出现显示不全)
rect = new RectF(ArcSize / 2, ArcSize / 2, getWidth() - ArcSize / 2, getHeight());
canvas.drawArc(rect, 135, 270, false, paint);
}
4.2 Draw the inner circle arc.
private void drawInnerLayer(Canvas canvas) {
paint.setColor(InnerLayerColor);
//防止初始化还未设置MaxValue时 出现除以0导致奔溃
if (MaxValue == 0)
return;
canvas.drawArc(rect, 135, (float) processValue / MaxValue * 270, false, paint);
}
4.3 Draw the middle text.
private void drawCentreText(Canvas canvas) {
//改回填充模式
paint.setStyle(Paint.Style.FILL);
//设置文字颜色
paint.setColor(StatisticsTextColor);
//设置文字大小
paint.setTextSize(StatisticsTextSize);
Rect bounds = new Rect();
//测量文字的宽高
paint.getTextBounds((processValue + ""), 0, (processValue + "").length(), bounds);
canvas.drawText(processValue + "", ((float) (getWidth() - bounds.width()) / 2), getHeight() / 2+bounds.height()/2, paint);
}
5. Use animation to move text and arcs.
asv = findViewById(R.id.asv);
asv.setMaxValue(5000);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 4800);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearOutSlowInInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
asv.setProcessValue((int) animatedValue);
}
});
valueAnimator.start();
}
The following is the entire code:
package com.zg.viewdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 圆弧统计的View
*/
public class ArcStatisticsView extends View {
private RectF rect;
/**
* 外层颜色值
*/
private int OuterLayerColor;
/**
* 内层颜色值
*/
private int InnerLayerColor;
/**
* 圆弧大小
*/
private int ArcSize;
/**
* 中间文字大小
*/
private int StatisticsTextSize;
/**
* 中间文字颜色值
*/
private int StatisticsTextColor;
/**
* 最大值
*/
private int MaxValue;
/**
* 当前值
*/
private int processValue;
public void setMaxValue(int maxValue) {
this.MaxValue = maxValue;
invalidate();
}
public void setProcessValue(int processValue) {
this.processValue = processValue;
invalidate();
}
private Paint paint = new Paint();
public ArcStatisticsView(Context context) {
this(context, null);
}
public ArcStatisticsView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ArcStatisticsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义的属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcStatisticsView);
OuterLayerColor = typedArray.getColor(R.styleable.ArcStatisticsView_OuterLayerColor, Color.RED);
InnerLayerColor = typedArray.getColor(R.styleable.ArcStatisticsView_InnerLayerColor, Color.GREEN);
ArcSize = (int) typedArray.getDimension(R.styleable.ArcStatisticsView_ArcSize, ArcSize);
StatisticsTextSize = (int) typedArray.getDimension(R.styleable.ArcStatisticsView_StatisticsTextSize, StatisticsTextSize);
StatisticsTextColor = typedArray.getColor(R.styleable.ArcStatisticsView_StatisticsTextColor, Color.GREEN);
typedArray.recycle();
}
//测量大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取设置的宽和高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//哪个值小就用哪个值正方形
setMeasuredDimension(widthSize > heightSize ? heightSize : widthSize, widthSize > heightSize ? heightSize : widthSize);
}
//绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 1.绘制外层圆弧
drawOuterLayer(canvas);
//2.绘制内层圆弧
drawInnerLayer(canvas);
//3.绘制中间部分文字
drawCentreText(canvas);
}
private void drawCentreText(Canvas canvas) {
//改回填充模式
paint.setStyle(Paint.Style.FILL);
//设置文字颜色
paint.setColor(StatisticsTextColor);
//设置文字大小
paint.setTextSize(StatisticsTextSize);
Rect bounds = new Rect();
//测量文字的宽高
paint.getTextBounds((processValue + ""), 0, (processValue + "").length(), bounds);
canvas.drawText(processValue + "", ((float) (getWidth() - bounds.width()) / 2), getHeight() / 2+bounds.height()/2, paint);
}
private void drawInnerLayer(Canvas canvas) {
paint.setColor(InnerLayerColor);
//防止初始化还未设置MaxValue时 出现除以0导致奔溃
if (MaxValue == 0)
return;
canvas.drawArc(rect, 135, (float) processValue / MaxValue * 270, false, paint);
}
private void drawOuterLayer(Canvas canvas) {
//设置画笔的属性
paint.setAntiAlias(true);
//设置画笔颜色
paint.setColor(OuterLayerColor);
//设置画笔大小
paint.setStrokeWidth(ArcSize);
//设置画笔模式(描边模式)
paint.setStyle(Paint.Style.STROKE);
//设置为圆头(如果不设置的话就会是直角边)
paint.setStrokeCap(Paint.Cap.ROUND);
//第四个参数控制是否和圆心连接起来
//创建一个矩阵 圆弧绘制在矩阵中(因为绘制的线条有宽度所以需要减去不然会出现显示不全)
rect = new RectF(ArcSize / 2, ArcSize / 2, getWidth() - ArcSize / 2, getHeight());
canvas.drawArc(rect, 135, 270, false, paint);
}
}
public class MainActivity extends AppCompatActivity {
private ArcStatisticsView asv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
asv = findViewById(R.id.asv);
asv.setMaxValue(5000);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 4800);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearOutSlowInInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
asv.setProcessValue((int) animatedValue);
}
});
valueAnimator.start();
}
}