val width = size.width
val radius = width / 2f
val strokeWidth = 20.dp.toPx()
val circumference = 2 * Math.PI * (radius - strokeWidth / 2)
val strokeAngle = (strokeWidth / circumference * 180f).toFloat()
drawArc(
color = Color.Blue,
startAngle = 180f + strokeAngle,
sweepAngle = 180f - strokeAngle * 2,
useCenter = false,
topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
size = Size(width - strokeWidth, width - strokeWidth),
style = Stroke(strokeWidth, cap = StrokeCap.Round)
)
drawArc(
color = Color.Red,
startAngle = 0f + strokeAngle,
sweepAngle = 180f - strokeAngle * 2,
useCenter = false,
topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
size = Size(width - strokeWidth, width - strokeWidth),
style = Stroke(strokeWidth, cap = StrokeCap.Round)
)
如果您希望增加弧之间的间距,可以添加系数。
val strokeAngle = 1.1f * (strokeWidth / circumference * 180f).toFloat()
这是一个示例,标签放置在带有圆角连接的弧形外部
@Preview
@Composable
private fun PieChartWithLabels() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
val chartDataList = listOf(
ChartData(Pink400, 10f),
ChartData(Orange400, 20f),
ChartData(Yellow400, 15f),
ChartData(Green400, 5f),
ChartData(Blue400, 50f),
)
Canvas(
modifier = Modifier
.fillMaxWidth(.7f)
.aspectRatio(1f)
) {
val width = size.width
val radius = width / 2f
val strokeWidth = 20.dp.toPx()
val circumference = 2 * Math.PI * (radius - strokeWidth / 2)
val strokeAngle = 1.1f * (strokeWidth / circumference * 180f).toFloat()
var startAngle = -90f
for (index in 0..chartDataList.lastIndex) {
val chartData = chartDataList[index]
val sweepAngle = chartData.data.asAngle
val angleInRadians = (startAngle + sweepAngle / 2).degreeToAngle
drawArc(
color = chartData.color,
startAngle = startAngle + strokeAngle,
sweepAngle = sweepAngle - strokeAngle * 2,
useCenter = false,
topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
size = Size(width - strokeWidth, width - strokeWidth),
style = Stroke(strokeWidth, cap = StrokeCap.Round)
)
startAngle += sweepAngle
val rectWidth = 20.dp.toPx()
drawRect(
color = Color.Red,
size = Size(rectWidth, rectWidth),
topLeft = Offset(
-rectWidth / 2 + center.x + (radius + strokeWidth) * cos(
angleInRadians
),
-rectWidth / 2 + center.y + (radius + strokeWidth) * sin(
angleInRadians
)
)
)
}
}
}
}
private val Float.degreeToAngle
get() = (this * Math.PI / 180f).toFloat()
@Immutable
data class ChartData(val color: Color, val data: Float)
获取圆周的数学原理是我们需要获取笔画宽度的中心,将这个圆变成带有圆角端点的线条,并正确测量笔画半径,其值为笔画宽度的一半。
如下图所示,我们正在获取半径,其中红线是用strokeWidth/ 2的宽度绘制的。
@Preview
@Composable
fun CanvasTest() {
Canvas(
modifier = Modifier
.padding(50.dp)
.fillMaxWidth()
.aspectRatio(1f)
// .border(1.dp, Color.Green)
) {
val strokeWidth = 80f
drawLine(
color = Color.Blue,
start = Offset(strokeWidth / 2, strokeWidth / 2),
end = Offset(200f - strokeWidth / 2, strokeWidth / 2),
strokeWidth = strokeWidth,
cap = StrokeCap.Round
)
drawArc(
color = Color.Red,
useCenter = false,
topLeft = Offset.Zero,
size = Size(strokeWidth, strokeWidth),
startAngle = 90f,
sweepAngle = 180f,
style = Stroke(2.dp.toPx())
)
drawLine(
color = Color.Red,
start = Offset(0f, strokeWidth / 2 + 1),
end = Offset(strokeWidth / 2, strokeWidth / 2 + 1),
strokeWidth = 2f
)
drawLine(
color = Color.Red,
start = Offset(200f - strokeWidth / 2, strokeWidth / 2 + 1),
end = Offset(200f, strokeWidth / 2 + 1),
strokeWidth = 2f
)
}
}
如果没有圆形帽,我们将整个绘制蓝线。有了圆形帽,我们在起始角度处偏移strokeAngle,以避免重叠,并删除-2 * strokeAngle以仅保留弧的空间,其中仅绘制蓝线。