Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save raghunandankavi2010/c7daf3b869b364e92de239fe1fb4dd64 to your computer and use it in GitHub Desktop.
Save raghunandankavi2010/c7daf3b869b364e92de239fe1fb4dd64 to your computer and use it in GitHub Desktop.
Custom Circular Progress view with percentage text in center.
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import androidx.core.content.ContextCompat
import bg.dihanov.customviewexamples.R
import bg.dihanov.customviewexamples.px
import kotlin.math.max
import kotlin.math.min
class CircularProgressView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
// required to draw the arcs
private val rectF = RectF()
private val r: Rect = Rect()
// Used to draw pretty much anything on a canvas, which is what we will be drawing on
private val paint = Paint().apply {
// how we want the arcs to be draw, we want to make sure the arc centers are not colored
// so we use a STROKE instead.
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
}
private val textPaint = Paint().apply {
style = Paint.Style.STROKE
color = Color.GREEN
val spSize = 18 //your sp size
// Convert the sp to pixels
val scaledTextSize = spSize * context?.let {
it.resources.displayMetrics.scaledDensity
}!!
textSize = scaledTextSize;
}
// default percentage set to 0
private var percentage = 0
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
var width = paddingLeft + paddingRight
var height = paddingTop + paddingBottom
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize
} else {
width += 200.px
width = max(width, suggestedMinimumWidth)
if (widthMode == MeasureSpec.AT_MOST) width = min(widthSize, width)
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize
} else {
height += 200.px
height = max(height, suggestedMinimumHeight)
if (heightMode == MeasureSpec.AT_MOST) height = min(height, heightSize)
}
setMeasuredDimension(width, height)
}
@SuppressLint("CanvasSize")
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.let {
rectF.apply {
val width = (it.width.div(2)).toFloat() // center X of the canvas
val height = (it.height.div(2)).toFloat() // center Y of the canvas
// place the rectF it at the center of the screen with height and width of 200dp
set(width - 200, height - 200, width + 200, height + 200)
}
// draw an arc that will represent an empty loading view
it.drawArc(rectF, 0f, 360f, false, paint.apply {
color = ContextCompat.getColor(context, R.color.grey)
// how wide the stroke should be, typically more than or equal to the strokeWidth
// of the arc representing a filled percentage
strokeWidth = 60f
})
// get the actual percentage as a float
val fillPercentage = (360 * (percentage / 100.0)).toFloat()
// draw the arc that will represent the percentage filled up
it.drawArc(rectF, 270f, fillPercentage, false, paint.apply {
color = ContextCompat.getColor(context, R.color.green) // filled percentage color
// how wide the stroke should be, typically less than or equal to the strokeWidth
// of the empty arc
strokeWidth = 60f
})
drawPercentage(it, textPaint, percentage.toString().plus("%"))
}
}
private fun drawPercentage(canvas: Canvas, paint: Paint, text: String) {
canvas.getClipBounds(r)
val cHeight: Int = r.height()
val cWidth: Int = r.width()
paint.textAlign = Paint.Align.LEFT
paint.getTextBounds(text, 0, text.length, r)
val x: Float = cWidth / 2f - r.width() / 2f - r.left
val y: Float = cHeight / 2f + r.height() / 2f - r.bottom
canvas.drawText(text, x, y, paint)
}
fun setPercentage(percentage: Int) {
this.percentage = percentage
invalidate()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment