Kotlin round progress bar


Custom round progress bar of kotlin version

Most start-up pages have a progress bar loading style, so I rewrite it with kotlin. If it’s really cool, there are still many things to add

A simple progress bar is basically composed of a background ring and a progress ring. What we need to pay attention to is the way of drawing

Because the circular progress bar is equal in width and height, we define that the width and height of view may not be equal, so we need to calculate the middle drawing when drawing, otherwise the drawing result may not be your ideal, such as the upper right corner below




Of course, if only two rings feel a little ugly and not very beautiful, you can add a little element to make it slightly beautiful. For example, draw a progress percentage prompt text, customize the color of the ring, and draw a center circle in the center. In this way, the color configuration is much more beautiful than two monotonous rings

Then you can also add a little initialization or end of the view animation, here I added a zoom animation, start to pick up the keyboard a shuttle

1. Initialization

Brush, color, background, font, progress and so on all need to use custom attributes, so these parameters need to be instantiated during initialization, and then the scaling animation just added is also in initialization

Define style in XML

Then write the init method

Kotlin round progress barKotlin round progress bar

private fun init() {
        //Custom properties
        val attributes: TypedArray = context.obtainStyledAttributes(
        mStrokeWidth = attributes.getDimension(R.styleable.circleProgress_circle_width, 1f)
        max = attributes.getFloat(R.styleable.circleProgress_circle_max, 100f)
        mProgress = attributes.getFloat(R.styleable.circleProgress_circle_progress, 0f)
        val bgColor = attributes.getColor(R.styleable.circleProgress_circle_color,Color.GRAY)
        val progressColor = attributes.getColor(R.styleable.circleProgress_circle_progress_color,Color.BLUE)
        val textColor = attributes.getColor(R.styleable.circleProgress_circle_text_color,Color.BLACK)
        val textSize = attributes.getDimension(R.styleable.circleProgress_circle_text_size,SizeUtils.dp2px(10f).toFloat())
        val isBold = attributes.getBoolean(R.styleable.circleProgress_circle_text_isBold,false)
        val centerColor = attributes.getColor(R.styleable.circleProgress_circle_center_color,Color.TRANSPARENT)
        //Circle brush
        bgPaint = Paint()
        bgPaint.style = Paint.Style.STROKE
        bgPaint.strokeWidth = mStrokeWidth
        bgPaint.color = bgColor
        bgPaint.isAntiAlias = true
        //Center circle brush
        centerPaint = Paint()
        centerPaint.style = Paint.Style.FILL
        centerPaint.color = centerColor
        centerPaint.isAntiAlias = true
        //Progress bar brush
        tintPaint = Paint()
        tintPaint.style = Paint.Style.STROKE
        tintPaint.strokeWidth = mStrokeWidth
        tintPaint.strokeCap = Paint.Cap.ROUND
        tintPaint.color = progressColor
        tintPaint.isAntiAlias = true
        //Writing brush
        textPaint = Paint()
        textPaint.style = Paint.Style.FILL
        textPaint.textSize = textSize
        textPaint.textAlign = Paint.Align.CENTER
        textPaint.isFakeBoldText = isBold
        textPaint.color = textColor
        textPaint.isAntiAlias = true


View Code

2. Measurement

Because the width and height are uncontrollable, you don’t know what the specific setting looks like. Some have the same width and height, and some have the width greater than the height. Therefore, the diameter of the circle needs to be the minimum value in order to draw a complete circle

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mWidth = getRealSize(widthMeasureSpec)
        mHeight = getRealSize(heightMeasureSpec)
        /**Diameter - equal width and height fill | when the width and height are inconsistent, take the smallest circle*/
        val diameter = min(mWidth,mHeight)
        mRadius = diameter / 2f - mStrokeWidth
        /**Progress bar drawing area*/
        val mX = mWidth/2f-diameter/2
        val mY = mHeight/2f-diameter/2
        mRect = RectF(mX + mStrokeWidth, mY + mStrokeWidth, mX + diameter - mStrokeWidth, mY + diameter - mStrokeWidth)



The circle is drawn directly by drawcircle, and the progress bar needs to draw a sector. Then, the angle of the sector is calculated by passing the progress bar. The percentage text is drawn at the center of the circle

override fun onDraw(canvas: Canvas?) {
        val progress = mProgress / max * 360
        //Draw a circle
        canvas!!.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, bgPaint)
        //Draw center circle
        canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, centerPaint)
        //Drawing progress
        canvas.drawArc(mRect, -90f, progress, false, tintPaint)
        //Draw text (percent)
        val percentage: Int = (mProgress / max * 100).toInt()
        val centerY = mHeight / 2 + mStrokeWidth / 2
        canvas.drawText("${percentage}%", mWidth / 2f, centerY, textPaint)

It’s that simple. The main logic is actually measurement and drawing

Then set the progress of external expansion and refresh the view




Here is a simulation of the loading progress, the effect is shown at the top



Recommended Today

Analysis on some problems of c# struct

catalogue Difference from class: Struct’s theory has been read better, but it has not been applied in work. Class exists everywhere. Doesn’t struct have any use value? Searched how to choose between classes and structures? ✔️ If the instance of a type is small and usually has a short lifetime or is usually embedded in […]