Reputation: 365
I've looking to draw an arc on a canvas in Jetpack Compose with a little circle on the edge of progress like this picture:
I found how to draw the progress bar with arc canvas but don't know yet how to draw the circle to match with the edge of the arc line. This is my progress code:
@Composable
fun ComposeCircularProgressBar(
modifier: Modifier = Modifier,
percentage: Float,
fillColor: Color,
backgroundColor: Color,
strokeWidth: Dp
) {
Canvas(
modifier = modifier
.padding(strokeWidth / 2)
) {
// Background Line
drawArc(
color = backgroundColor,
135f,
270f,
false,
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Butt)
)
// Fill Line
drawArc(
color = fillColor,
135f,
270f * percentage,
false,
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)
)
}
}
Noted: for now I know to draw that circle is with Canvas.drawCircle(offset = Offset)
but I don't know yet how to calculate the Offset(x,y)
to match with the edge of progress.
Upvotes: 16
Views: 6998
Reputation: 2307
This piece of code below will generate the arc with the circular dot based on the percentage you provide. You did get most of the part right, it was just about solving the Math Equation to find the point on the circle.
I assumed the radius of the circle as Height / 2 of the widget.
Since we are not drawing the full circle, the start angle is at 140 degrees and the maximum sweep angle is 260 degrees. (I found this by hit and trial, so that it looks as close to your image)
Now to draw the small white circle the center a.k.a offset has to be at (x,y) where x & y are given by the formula
x = radius * sin (angle in radians) y = radius * cos (angle in radians)
@Composable
fun ComposeCircularProgressBar(
modifier: Modifier = Modifier,
percentage: Float,
fillColor: Color,
backgroundColor: Color,
strokeWidth: Dp
) {
Canvas(
modifier = modifier
.size(150.dp)
.padding(10.dp)
) {
// Background Line
drawArc(
color = backgroundColor,
140f,
260f,
false,
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round),
size = Size(size.width, size.height)
)
drawArc(
color = fillColor,
140f,
percentage * 260f,
false,
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round),
size = Size(size.width, size.height)
)
var angleInDegrees = (percentage * 260.0) + 50.0
var radius = (size.height / 2)
var x = -(radius * sin(Math.toRadians(angleInDegrees))).toFloat() + (size.width / 2)
var y = (radius * cos(Math.toRadians(angleInDegrees))).toFloat() + (size.height / 2)
drawCircle(
color = Color.White,
radius = 5f,
center = Offset(x, y)
)
}
}
Here are some examples I tried with
@Preview
@Composable
fun PreviewPorgressBar() {
ComposeCircularProgressBar(
percentage = 0.80f,
fillColor = Color(android.graphics.Color.parseColor("#4DB6AC")),
backgroundColor = Color(android.graphics.Color.parseColor("#90A4AE")),
strokeWidth = 10.dp
)
}
@Preview
@Composable
fun PreviewPorgressBar() {
ComposeCircularProgressBar(
percentage = 0.45f,
fillColor = Color(android.graphics.Color.parseColor("#4DB6AC")),
backgroundColor = Color(android.graphics.Color.parseColor("#90A4AE")),
strokeWidth = 10.dp
)
}
@Preview
@Composable
fun PreviewPorgressBar() {
ComposeCircularProgressBar(
percentage = 1f,
fillColor = Color(android.graphics.Color.parseColor("#4DB6AC")),
backgroundColor = Color(android.graphics.Color.parseColor("#90A4AE")),
strokeWidth = 10.dp
)
}
[Update] If you're interested in a step-by-step tutorial you can read it here : https://blog.droidchef.dev/custom-progress-with-jetpack-compose-tutorial/
Upvotes: 19