нарисовать закругленные углы на холсте с помощью drawArc

Я пытаюсь создать круговую диаграмму в Jetpack Compose. Я пытаюсь скруглить углы для каждого круга на диаграмме. Но у меня проблемы с поворотами по углам. Я попробовал использоватьcap = StrokeCap.RoundвdrawArcв холсте, но мне не удалось скруглить только углы.

Это то, что у меня есть на данный момент, и результат выглядит следующим образом. Как видите, углы каждого пирога имеют прямоугольную форму. Есть ли способ сделать их круглыми?

      @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()
    }
}

Я пытаюсь сделать так, чтобы дуга выглядела вот так

2 ответа

Проблема заключается в том, как вы структурируете зазор между дугами, поэтому вы не видите закругленных краев приStrokeCap.Round. Вам нужно сделать это как

      startAngle = startAngle + gap,

и SweepAngle как

      sweepAngle = sweepAngle - gap * 2,

и когда у тебя будет достаточно пробелов в раунде

      @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
                val gap = 25f/2

                drawArc(
                    color = Color.Gray,
                    startAngle = startAngle + gap,
                    sweepAngle = sweepAngle - gap * 2,
                    useCenter = false,
                    topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
                    size = Size(width - strokeWidth, width - strokeWidth),
                    style = Stroke(strokeWidth, cap = StrokeCap.Round)
                )

                startAngle += sweepAngle
            }
        }
    }
}

Я подробно объяснил логику в этом ответе

/questions/65146552/jetpack-sostavlyaet-canvas-arch-extra-stroke/65967656#65967656

  • Я нашел обходной путь, нарисовав две дуги, одну сstyle = Fillи один сstyle = Stroke. Надеюсь, это кому-то поможет.

  • Другой способ сделать это без использования хода — использовать сложный триггер. Это включает в себя преобразование между полярными и декартовыми координатами.

  • Используя эти формулы и зная внешний/внутренний радиус и угол, вы можете рассчитать все 4 угла дуги.

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

используя приведенную выше математику, вы сможете найтиx and yточки дуги. затем используяPathты можешь рисовать

      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()
}

// Пример с обводкой..

      @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
            }
        }
    }
}