How to draw a Ring in Jetpack Compose with Canvas?

613 Views Asked by At

I know you can draw a circle in Jetpack Compose and Canvas using simple methods. I tried drawing a colored circle and another smaller white circle to achieve a ring but it doesn't seem right. What if the background is not white or the ring shape should drawn on other things and the inside of it needs to be really hollow?

Surface {
        Canvas(
            modifier = Modifier.size(200.dp),
            onDraw = {
                drawCircle(
                    color = Color.Blue,
                    radius = 200f
                )
                drawCircle(
                    color = Color.White,
                    radius = 150f
                )
            }
        )
    }

fake ring

2

There are 2 best solutions below

2
On BEST ANSWER

You can set style as Stroke to draw a non-filled shape.

enter image description here

@Preview
@Composable
private fun RingSample() {

    Canvas(
        modifier = Modifier.size(200.dp),
        onDraw = {

            val strokeWidth = 50f
            drawCircle(
                color = Color.Blue,
                radius = 200f,
                style = Stroke(
                    width = strokeWidth
                )
            )

        }
    )
}
0
On

I realized using Canvas and Paths many shapes can be drawn.

real ring

@Preview(showBackground = true)
@Composable
fun RingPreview() {
    Surface {
        Ring(modifier = Modifier.padding(8.dp))
    }
}

@Composable
fun Ring(
    modifier: Modifier = Modifier,
    diameter: Dp = 100.dp,
    @FloatRange(from = 0.0, 1.0)
    ringSize: Float = .1f,
    color: Color = MaterialTheme.colorScheme.primary
) {
    Canvas(
        modifier = modifier.size(diameter),
        onDraw = {
            drawRing(
                color = color,
                diameter = diameter,
                ringFraction = ringSize
            )
        }
    )
}

fun DrawScope.drawRing(
    color: Color,
    diameter: Dp = 100.dp,
    @FloatRange(from = 0.0, 1.0)
    ringFraction: Float = .1f,
    offset: Offset = Offset.Zero
) {
    val path = Path().apply {
        val size = diameter.toPx()
        addOval(Rect(0f, 0f, size, size))
        op(
            path1 = this,
            path2 = Path().apply {
                addOval(
                    Rect(0f, 0f, size * (1 - ringFraction), size * (1 - ringFraction))
                )
                translate(Offset(size * ringFraction / 2, size * ringFraction / 2))
            },
            operation = PathOperation.Difference
        )
        if (offset != Offset.Zero)
            translate(offset.copy(offset.x - size / 2, offset.y - size / 2))
    }
    drawPath(path, color)
}