androidandroid-jetpack-composeandroid-jetpackandroid-jetpack-compose-canvasandroid-compose-layout

draw round corners in canvas with drawArc


I'm trying to create a pie chart in Jetpack Compose. I'm trying to make corners round for each Pie in the Chart. But, I'm having issues making corner rounds. I tried using cap = StrokeCap.Round in drawArc in canvas, but did not get any luck making just the corners rounds.

This is what I have so far, and the result looks like this. As you can see the corners of each pies are rectangle. Is there a way to make them round?

enter image description here

@Composable
fun Chart() {
    Canvas(
        modifier = Modifier
            .fillMaxWidth()
            .aspectRatio(1f)
    ) {
        drawIntoCanvas {
            val width = size.width
            val radius = width / 2f
            val strokeWidth = radius * .3f
            var startAngle = 0f

            val items = listOf(25f, 25f, 25f, 25f, 25f, 25f, 25f, 25f)

            items.forEach {
                val sweepAngle = it.toAngle

                drawArc(
                    color = Color.Gray,
                    startAngle = startAngle,
                    sweepAngle = sweepAngle - 5,
                    useCenter = false,
                    topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
                    size = Size(width - strokeWidth, width - strokeWidth),
                    style = Stroke(strokeWidth)
                )

                startAngle += sweepAngle
            }
        }
    }
}

private val Float.toAngle: Float
    get() = this * 180 / 100

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        Chart()
    }
}

I’m trying to make it so the arc looks like this

enter image description here


Solution

  • x1 = r1 cos (πœƒ)
    y1 = r1 sin (πœƒ)
    x2 = r2 cos (πœƒ)
    y3 = r2 sin (πœƒ)
    where radius = r1 = outerRadius, r2 = innerRadius and πœƒ = Math.toRadians(sweepAngle)
    

    using the math above you should be able to find x and y points of an arc. then using Path you can draw

    Path().apply{
       moveTo(x, y) // start of arc in top left - rounded edge
       arcTo(Rect(..), startAngle, sweepAngle, false) // or you can use `quadTo` or `curveTo` but you'll have to find control points
       quadTo(..) // to draw the rounded corner - top right
       lineTo(..) // line to next point - bottom right
       quadTo(..) // draw rounded corner in bottom right
       arcTo() // inner arc to bottom left corner - rounded edge
       quadTo() // bottom left corner
       lineTo() // line to top left corner
       quadTo() // top left corner
       close()
    }
    

    // Example with stroke..

    @Composable
    fun Chart() {
        Canvas(
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(1f)
        ) {
            drawIntoCanvas {
                val width = size.width
                val radius = width / 2f
                val innerRadius = radius - 40f
             
                var startAngle = 0f
                val center = Offset(size.width / 2f, size.height / 2f)
    
                val items = listOf(25f, 25f, 25f, 25f, 25f, 25f, 25f, 25f)
    
                items.forEach {
                    val sweepAngle = it.toAngle
    
                    val path = Path().apply{
                       drawArc(
                          Rect(
                            center.x - radius,
                            center.y - radius,
                            center.x + radius,
                            center.y + radius
                          ),
                          startAngle, 
                          sweepAngle,
                          false
                       )
                        drawArc(
                          Rect(
                            center.x - innerRadius,
                            center.y - innerRadius,
                            center.x + innerRadius,
                            center.y + innerRadius
                          ),
                          startAngle + sweepAngle, 
                          -sweepAngle,
                          false
                       )
                    }
                    
                    drawPath(path, Color.Red, style = Fill)
                    drawPath(path, Color.Red, style = Stroke(30f, StrokeCap.Round, StrokeJoin.Round))
    
                    startAngle += sweepAngle
                }
            }
        }
    }