DEV Community

Cover image for A custom circular progress indicator using the Canvas and animation
Terrence Aluda
Terrence Aluda

Posted on

A custom circular progress indicator using the Canvas and animation

In this post, we will see how to build a custom circular progress indicator using the Canvas. We will be emulating the Samsung Galaxy Watch charging display screen.

Image description

Get the full code on GitHub.

The building blocks for the canvas are arcs. One will superimpose the other, as shown below.

Image description

The foundational arc will have a grayish background, while the one positioned at the top contains two gradient colors with shades of green. The top arc is the one we will animate to show the charging progress.

Creating the arcs

We will use the drawArc method. Below is the signature of the drawArc method.

fun drawArc(
    brush: Brush,
    startAngle: Float,
    sweepAngle: Float,
    useCenter: Boolean,
    topLeft: Offset = Offset.Zero,
    size: Size = this.size.offsetSize(topLeft),
    alpha: @FloatRange(from = 0.0, to = 1.0) Float = 1.0f,
    style: DrawStyle = Fill,
    colorFilter: ColorFilter? = null,
    blendMode: BlendMode = DefaultBlendMode
): Unit
Enter fullscreen mode Exit fullscreen mode

We will only be using the brush, startAngle, sweepAngle, useCenter,topLeft, size, and style parameters. In a nutshell:

  • brush is used for setting the color gradient. There is also another variation that accepts color instead of brush, and that's what we will use for the gray arc.
  • startAngle for setting the angle the arc will start at
  • sweepAngle for setting the angle the arc will complete at
  • useCenter is used to determine if we want our arc to be a sector or an open arc. In our case, we will need it to be open, so we will use the false value
  • topLeft is used to set offsets(displacements of the arc), which in our case we won't need, so our offsets will be zero.
  • size is for setting the arc's dimensions
  • style is for setting the arcs styling. For instance, in this case will be setting the endpoints to be round and a width of 15.dp

Enough with the basics. Let's begin adding the arcs.

For the gray arc, we set it to be a full circle(360°) and let it occupy the full parent's width.

drawArc(
    color=Color(0xFF373a37),
    0f,
    360f,
    topLeft=Offset(x=0.dp.toPx(), y=0.dp.toPx()),
    useCenter=false,
    size=Size(size.width, size.height),
    style=Stroke(15.dp.toPx(), cap=StrokeCap.Round))
Enter fullscreen mode Exit fullscreen mode

For the green arc, we will set a gradient, and let it start from -110° to 270°. I will talk about the Android coordinate system in detail in a different post. The degrees choices are based on your preference, the same as the color gradient.

...
val gradient=Brush.verticalGradient(
            colorStops=arrayOf(
            0.0f to Color.Transparent,
            1.0f to Color(0xFF0cdc35)))
...

drawArc(brush=gradient,
    -110f,
    270f,
    topLeft=Offset(x=0.dp.toPx(), y=0.dp.toPx()),
    useCenter=false,
    size=Size(size.width, size.height),
    style=Stroke(15.dp.toPx(), cap=StrokeCap.Round))
Enter fullscreen mode Exit fullscreen mode

We are done with the arcs, let's proceed to add the animation.

Adding the animation (rotating the arc)

We will need to add a rotation animation using the InfiniteTransition.animateFloat() method, whose foundational workings are shown in the documentation.

This is our implementation.

...
val infiniteTransition = rememberInfiniteTransition(label = "")
...

val rotation by infiniteTransition.animateFloat(
    initialValue=0f,
    targetValue=360f,
    animationSpec=infiniteRepeatable(
        animation=tween<Float>(durationMillis=1500,
            easing=LinearEasing,
        ),
        repeatMode=RepeatMode.Restart), 
        label="Rotate it"
)
Enter fullscreen mode Exit fullscreen mode

We will wrap it around the second arc, as shown below.

rotate(rotation) {
    drawArc(
        //...the rest of the code
    )
}
Enter fullscreen mode Exit fullscreen mode

And that's it! We are done with the arcs and the animation. The rest of the components, such as the icon and the text, are in the code I linked above. You can view the running app here.

Thank you for reading. Let me hear your thoughts in the comments. Until next time, happy coding! 🎨✨

Top comments (0)