Simple custom a more general circular progress bar, you can define the radius of the circle, progress color, width, middle font and other information. Here I will explain it step by step:

1, first of all, we must first find out what attributes need to custom, the progress bar color, progress, the progress bar radius, width of progress, and progress bar in the text color and size, maximum progress, the current schedule, then I added a directional properties, said progress direction where to begin (the default has four directions, lower left on the right), Once this is done, we define it in attrs:

<declare-styleable name="CustomCircleProgressBar"> <attr name="outside_color" format="color" /> <attr name="outside_radius" format="dimension" /> <attr name="inside_color" format="color" /> <attr name="progress_text_color"  format="color" /> <attr name="progress_text_size" format="dimension" /> <attr name="progress_width" format="dimension" /> <attr name="max_progress" format="integer" /> <attr name="progress" format="float" /> <attr name="direction"> <enum name="left" value="0" /> <enum name="top" value="1" /> <enum name="right" value="2" /> <enum name="bottom" value="3" /> </attr> </declare-styleable>Copy the code

2. Then get these values in the constructor of your custom View:

public CustomCircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomCircleProgressBar, defStyleAttr, 0); outsideColor = a.getColor(R.styleable.CustomCircleProgressBar_outside_color, ContextCompat.getColor(getContext(), R.color.colorPrimary)); outsideRadius = a.getDimension(R.styleable.CustomCircleProgressBar_outside_radius, DimenUtil.dp2px(getContext(), 60.0 f)); insideColor = a.getColor(R.styleable.CustomCircleProgressBar_inside_color, ContextCompat.getColor(getContext(), R.color.inside_color)); progressTextColor = a.getColor(R.styleable.CustomCircleProgressBar_progress_text_color, ContextCompat.getColor(getContext(), R.color.colorPrimary)); progressTextSize = a.getDimension(R.styleable.CustomCircleProgressBar_progress_text_size, DimenUtil.dp2px(getContext(), 14.0 f)); progressWidth = a.getDimension(R.styleable.CustomCircleProgressBar_progress_width, DimenUtil.dp2px(getContext(), 10.0 f)); Atul gawande at the progress = etFloat (R.s tyleable CustomCircleProgressBar_progress, 50.0 f); maxProgress = a.getInt(R.styleable.CustomCircleProgressBar_max_progress, 100); direction = a.getInt(R.styleable.CustomCircleProgressBar_direction, 3); a.recycle(); paint = new Paint(); }Copy the code

3. Next we’ll rewrite the onMeasure method so it can adapt to your Settings:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width;
    int height;
    int size = MeasureSpec.getSize(widthMeasureSpec);
    int mode = MeasureSpec.getMode(widthMeasureSpec);

    if (mode == MeasureSpec.EXACTLY) {
        width = size;
    } else {
        width = (int) ((2 * outsideRadius) + progressWidth);
    }
    size = MeasureSpec.getSize(heightMeasureSpec);
    mode = MeasureSpec.getMode(heightMeasureSpec);
    if (mode == MeasureSpec.EXACTLY) {
        height = size;
    } else {
        height = (int) ((2 * outsideRadius) + progressWidth);
    }
    setMeasuredDimension(width, height);
}
Copy the code

4, these two pieces will not say more, I believe that most of the viewers should know, next we will analyze what to draw? How to draw? Set the hollow property of the brush and then set the width of the line to draw a circle:

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int circlePoint = getWidth() / 2; Paint. SetColor (insideColor); // Set the color of the circle paint.setstyle (STROKE); SetStrokeWidth (progressWidth); // Set the circle width paint.setantiAlias (true); DrawCircle (circlePoint, circlePoint, outsideRadius, paint); // Draw the circle}Copy the code

! [](//upload-images.jianshu.io/upload_images/490111-f1650614c8e2523a.png? imageMogr2/auto-orient/strip|imageView2/2/w/430/format/webp)

5, Then we draw the outer progress, the outer progress is an arc, according to the progress we get and the total progress to draw this arc, using the canvas. DrawArc () method, this method has two overload methods:

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) drawArc(float left, float top, Float right, float bottom, float startAngle, float sweepAngle, Booleusecenter,Paint Paint) actually, you can view it as a method, RectF: Left top right bottom RectF: Left top right bottom RectF: Left top right bottom

RectF holds four float coordinates for a rectangle. The rectangle isrepresented by the coordinates of its 4 edges (left,  top, right bottom). These fields can be accessed directly. Use width() and height() to retrieve the rectangle's width and height. Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).Copy the code

So basically, this thing can make a rectangle. How do you make a rectangle? I don’t know, hahaha! Let’s take a look at the constructor:

@param left   The X coordinate of the left side of the rectangle
@param top    The Y coordinate of the top of the rectangle
@param right  The X coordinate of the right side of the rectangle
@param bottom The Y coordinate of the bottom of the rectangle
Copy the code

Here to explain, rectangle has four points, the four value is the coordinates of the rectangle four points to determine the left said X coordinate of the rectangle on the left two point right said X coordinate of the rectangle two dots on the right top said rectangle Y coordinate of the two points above bottom said Y coordinate of the rectangle below two points detailed reference below:

! [](//upload-images.jianshu.io/upload_images/490111-e65494642466b8e4.png? imageMogr2/auto-orient/strip|imageView2/2/w/610/format/webp)

Top left and bottom right of RectF

Writing a:  RectF oval = new RectF(circlePoint - outsideRadius, circlePoint - outsideRadius, circlePoint + outsideRadius, circlePoint + outsideRadius); RectF oval = new RectF(); oval.left=circlePoint - outsideRadius; oval.top=circlePoint - outsideRadius; oval.right=circlePoint + outsideRadius; oval.bottom=circlePoint + outsideRadius;Copy the code

Then the second (fifth) parameter in the drawArc() method, startAngle, indicates the Angle at which we draw the radian, with values 0-360, 0 for three o ‘clock, 90 for six o ‘clock, and so on; The sweepAngle parameter is how many radians we want to draw, and it’s going to be from 0 to 360, so we’re going to use what percentage of the total progress we’re making, and then we’re going to multiply that by 360 and that’s how many radians we want to draw; The useCenter parameter indicates whether to draw with the center of the circle. Here’s the code:

SetColor (outsideColor); setColor(outsideColor); RectF oval = new RectF(circlePoint-outsideradius, circlePoint-outsideradius, circlePoint + outsideRadius, circlePoint + outsideRadius); / / is used to define the boundaries of circular arc shape and size of canvas. DrawArc (oval, CustomCircleProgressBar. DirectionEnum. GetDegree (direction), 360 * (progress / maxProgress), false, paint); // Draw an arc according to the progressCopy the code

! [](//upload-images.jianshu.io/upload_images/490111-7e4b5289589e4a3f.png? imageMogr2/auto-orient/strip|imageView2/2/w/264/format/webp)

Do not connect the center of the circle

Paint. SetColor (outsideColor); RectF oval = new RectF(circlePoint-outsideradius, circlePoint-outsideradius, circlePoint + outsideRadius, circlePoint + outsideRadius); / / is used to define the boundaries of circular arc shape and size of canvas. DrawArc (oval, CustomCircleProgressBar. DirectionEnum. GetDegree (direction), 360 * (progress / maxProgress), true, paint); // Draw an arc according to the progressCopy the code

! [](//upload-images.jianshu.io/upload_images/490111-fbb8b084df3a37c5.png? imageMogr2/auto-orient/strip|imageView2/2/w/266/format/webp)

Connection of the circle

6, the next is to draw the percentage of text in the circle, some people may say, drawText, it is not very simple, direct drawText method is not good, what value to pass what! Big brother don’t rush sa, below give everyone to see the effect of direct painting text:

paint.setColor(progressTextColor);
paint.setTextSize(progressTextSize);
paint.setStrokeWidth(0);
progressText = (int) ((progress / maxProgress) * 100) + "%";
canvas.drawText(progressText, circlePoint , circlePoint, paint);
Copy the code

! [](//upload-images.jianshu.io/upload_images/490111-39922ed83c633a5c.png? imageMogr2/auto-orient/strip|imageView2/2/w/302/format/webp)

WTF, what happened?? The drawText method starts at the top left corner of the text, so we need to cut out half the width and height of the text:

rect = new Rect();
paint.getTextBounds(progressText, 0, progressText.length(), rect);
canvas.drawText(progressText, circlePoint- rect.width() / 2 , circlePoint- rect.height() / 2, paint);
Copy the code

! [](//upload-images.jianshu.io/upload_images/490111-976c527b3d0bf1ea.png? imageMogr2/auto-orient/strip|imageView2/2/w/311/format/webp)

WTF again, and some people may say, LZ you lie, it is not in the center, the heart of the hell suddenly there are ten thousand grass mud horse in the pentium, don’t worry, not finished, give you a look at the source code you know:

/** * Draw the text, with origin at (x,y), using the specified paint. The * origin is interpreted based on the Align setting in the paint. * * @param text The text  to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); }Copy the code

I don’t know if you have seen the explanation of the third parameter Y, it is not the coordinate of the vertical axis, but the y coordinate of the base line, as for this base line, LZ is not going to expand here, because this also has a lot of content, to recommend a very detailed blog: custom control drawing (five) : DrawText () drawText() drawText() drawText()

Rect = new rect (); paint.setColor(progressTextColor); paint.setTextSize(progressTextSize); paint.setStrokeWidth(0); progressText = getProgressText(); paint.getTextBounds(progressText, 0, progressText.length(), rect); Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top; DrawText (progressText, getMeasuredWidth() / 2-rect.width () / 2, baseline, paint); drawText(progressText, getMeasuredWidth() / 2-rect.width () / 2, baseline, paint);Copy the code

And look at the final result:

! [](//upload-images.jianshu.io/upload_images/490111-bb07e687be2b896a.png? imageMogr2/auto-orient/strip|imageView2/2/w/319/format/webp)

Okay, now the progress and words are drawn, personal feel so directly show in front of the user to appear a little stiff, is there any way to make it from scratch running on schedule, we will set up the progress of the value and the answer is yes, here we can use the property animation, the first few blog we have when it comes to the knowledge of the property animation, If you haven’t seen it yet, move on:

Android custom view property animation familiar

Android custom view properties animation first look

Here we use ValueAnimator to set the progress of the ring by listening for the animation to change the progress value:

private void startAnim(float startProgress) { animator = ObjectAnimator.ofFloat(0, startProgress); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { CustomCircleProgressBar.this.progress = (float) animation.getAnimatedValue(); postInvalidate(); }}); animator.setStartDelay(500); // Set delayed start animator.setDuration(2000); animator.setInterpolator(new LinearInterpolator()); // Animator.start (); }Copy the code

This completes the custom prototype progress bar.

The original link: www.jianshu.com/p/45c44ad92…