Search code examples
androidandroid-jetpack-composeandroid-canvasandroid-jetpack-compose-canvas

Android: compose drawArc would not get centered within `Box`


I am trying to show progress using drawArc for compose. I have tried drawBehind modifier to draw a background circle and not trying to draw another circle on top of it to show the progress. The problems is, no matter what I try, the arc turns out to be drawn at top left corner all the time. If I explicitly define the topLeft value, it only works for 1 screen size. So I am trying to get a dynamic size for both circles and also the strokewidth. So for Tablets only the circles should be increased depending on the screensize and also the thickness should be increased for the same. And for the smaller size devices, the value should be decreasing. Here is my code and output:

Column(
        modifier = Modifier
            .fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        BoxWithConstraints(
            Modifier
                .fillMaxSize()
                .background(Color.Yellow),
        ) {
            Canvas(modifier = Modifier
                .size(maxWidth, maxHeight)
                .background(color = Color.Red)) {
                drawCircle(
                    color = Color.Gray,
                    radius = (maxWidth /4).toPx(),
                    style = Stroke(width = 14f, cap = StrokeCap.Round),
                )
                val sweepAngle = progress/100 * 360
                drawArc(
                    size = Size((maxWidth/2).toPx(),(maxWidth/2).toPx()),
                    color = Color.Green,
                    startAngle = -90f,
                    sweepAngle = sweepAngle,
                    useCenter = false,
                    style = Stroke(10f, cap = StrokeCap.Round),
                )
            }
        }
    }

enter image description here

PS: I cannot use another circle object since I need to have the tip of the circle round aka cap should be Stroke.Round.

I ended up trying BoxWithConstraints so I can have access to maxWidth and maxHeight


Solution

  • You have to calculate the topLeft offset of the arc as center of the circle - radius of the circle.
    Then the size of arc is maxWidth/2 instead of maxWidth/4 = radius.

    Something like:

                val stroke = 5.dp
                drawCircle(
                    color = Color.Gray,
                    radius = (maxWidth /4).toPx(),
                    style = Stroke(width = stroke.toPx(), cap = StrokeCap.Round),
                )
    
                val sweepAngle = progress/100 * 360
                val offsetx = this.center.x - (maxWidth/4).toPx() 
                val offsety = this.center.y - (maxWidth/4).toPx() 
    
                drawArc(
                    size = Size((maxWidth/2).toPx(),(maxWidth/2).toPx()),
                    color = Color.Green,
                    startAngle = 0f,
                    sweepAngle = -sweepAngle,
                    topLeft = Offset(offsetx,offsety),
                    useCenter = false,
                    style = Stroke(stroke.toPx(), cap = StrokeCap.Round),
                )
            }
    

    enter image description here