Custom doughnut progress bar

To draw a circle with a custom control, OnDraw() is used to draw the view, so as to output the view control that meets your needs

Observe the components of the ring:

Outer circle + middle percentage text + arc circle with changing progress

--->Analysis: the attributes required by each component constitute several key user-defined attributes

1: Color of outer circle

2: Color of arc progress circle

3: The color of the middle percentage text

4: Middle percentage text size

5: Width of the circle (as the width of the progress arc circle)

6: * there is also a circle progress in the home page. In order to be compatible with the circle progress of the home page, a custom attribute is added to draw the style of progress arc circle (solid [Fill], hollow [stroke])

Analysis finished -- > drawing steps:

1: Initialize the brush object in the constructor to get the custom attribute value

2: Override Ondraw method

---2.1: draw the outermost circle

-Key method canvas drawCircle(center, center, radius, paint); // Draw a circle

*: calculation radius, center point coordinates, brush settings

Center point coordinates

int center = getWidth() / 2; // Gets the x coordinate of the center of the circle


int radius = (int) (center - roundWidth/2) -- drawing description is the easiest to understand

---2.2: draw percentage text in the middle

--Key method: canvas drawText(percent + "%", center - textWidth / 2, center + textSize / 2, paint); // Draw progress percentage

Measure the width of the text on the brush

float textWidth = paint.measureText(percent + "%");

Brush settings

The position of the drawn text is determined by the X and Y coordinate values of parameters 2 and 3 -- the position of the center point of the ring is displayed

10: Indicates where to start drawing. If you start drawing directly from the center point -- > the drawing description is the easiest to understand

-->Correct X=center - textWidth / 2; Y = center + textSize / 2 -- (because the y-axis value of the android coordinate system is opposite to that of the mathematical coordinate system, you can also draw a picture to illustrate that the textsize here can represent the height. After the paint.measureText measurement method is executed, the default text height is calculated according to the text size, which is equivalent to wrap_content, so textsize is the height value occupied by its own text)

*: the progress of drawing should be converted to percentage form: int percent = (int) (((float) progress / (float) max) * 100);

---2.3: draw progress arc circle

---Key method: canvas drawArc(oval, 0, 360 * progress / max, false, paint); // Draw an arc according to the progress

Parameter interpretation:

oval: the range profile of the drawn arc

0: from what angle do you start drawing

360 * progress / max: draw the area corresponding to the angle swept by the arc

false: does not contain the center of the circle. If true, it means that the center of the circle is included

paint: brush used for painting

Brush settings

paint.setStrokeWidth(roundWidth); // When setting the width of the progress arc circle, it must be consistent with the StrokeWidth of the outer circle to ensure that the coverage of the arc circle is the width of the outer circle

paint.setColor(roundProgressColor); // Sets the color of the progress

Arc range calculation

RectF oval = new RectF(center - radius, center - radius, center

+ radius, center + radius); ---> Drawing instructions are the easiest to understand

*Note: because progress is relative to the proportion of 100, and the arc is divided into 360 points according to the angle, the angle of the area swept by the specified parameters needs to be calculated and converted

=360 * progress / max(max=100)

Finally, it provides a method to set the progress and redraw the ring according to the progress

..... Circle drawing custom control analysis end|

public class RoundProgress extends View {
    private Paint paint = new Paint();
    private int roundColor;
    private int roundProgressColor;
    private int textColor;
    private float textSize;
    private float roundWidth;
    private int max = 100;
    private int progress = 50;
    public RoundProgress(Context context) {
        this(context, null);
    public RoundProgress(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    public RoundProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgress);
        //The color of the ring
        roundColor = mTypedArray.getColor(R.styleable.RoundProgress_roundColor, Color.RED);
        //The color of the circle progress
        roundProgressColor = mTypedArray.getColor(R.styleable.RoundProgress_roundProgressColor, Color.GREEN);
        //The color of the intermediate progress percentage text string
        textColor = mTypedArray.getColor(R.styleable.RoundProgress_textColor, Color.GREEN);
        //The font size of the string for the intermediate progress percentage
        textSize = mTypedArray.getDimension(R.styleable.RoundProgress_textSize, 15);
        //Width of the ring
        roundWidth = mTypedArray.getDimension(R.styleable.RoundProgress_roundWidth, 5);
    protected void onDraw(Canvas canvas) {
        //Step 1: draw an outermost circle
        int center = getWidth() / 2;
        int radius = (int) (center - roundWidth / 2);
        canvas.drawCircle(center, center, radius, paint);
        //Step 2: draw the text in the middle
        float textWidth = paint.measureText(progress + "%");
        canvas.drawText(progress + "%", center - textWidth / 2, center + textSize / 2, paint);
        //Step 3:
         * Parameter interpretation:
         * oval: Sketch the outline of the rectangular range contained by the arc circle
         * 0: Starting angle
         * 360 * progress / max: Scanned angle
         * false: Include center point
         * paint: Brush when drawing an arc
        RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius);
        canvas.drawArc(oval, 0, 360 * progress / max, false, paint);
    public void setProgress(int progress){
        this.progress = progress;
            this.progress = 100;
<?xml version="1.0" encoding="utf-8"?>
    <declare-styleable name="RoundProgress">
        <attr name="roundColor" format="color"/>
        <attr name="roundProgressColor" format="color"/>
        <attr name="roundWidth" format="dimension"></attr>
        <attr name="textColor" format="color"/>
        <attr name="textSize" format="dimension"/>
  <!-- Circle progress-->
 private Runnable runnable = new Runnable() {
        public void run() {
            int tempProgress = 0;
            try {
                while (tempProgress <= totalProgress) {
            } catch (InterruptedException e) {

Added by coolfool on Wed, 12 Jan 2022 09:56:47 +0200