如何在Jetpack Compose中创建带圆弧端点的六边形形状
实现带圆弧端点的Jetpack Compose六边形形状
要给六边形的尖角替换成圆弧端点,核心思路是在每个顶点的两条边上,分别取距离顶点等于圆角半径的点,然后以原顶点为圆心、圆角半径为半径,用圆弧连接这两个点。以下是完整实现代码:
修改后的Shape类与路径绘制
class HexagonShape(private val cornerRadius: Float) : Shape { override fun createOutline( size: Size, layoutDirection: LayoutDirection, density: Density ): Outline { return Outline.Generic( path = drawCustomHexagonPath(size, cornerRadius) ) } } private fun drawCustomHexagonPath(size: Size, cornerRadius: Float): Path { return Path().apply { val radius = min(size.width / 2f, size.height / 2f) val triangleHeight = (kotlin.math.sqrt(3.0) * radius / 2).toFloat() val centerX = size.width / 2f val centerY = size.height / 2f // 定义六边形的六个基础顶点 val points = listOf( Offset(centerX, centerY + radius), Offset(centerX - triangleHeight, centerY + radius / 2f), Offset(centerX - triangleHeight, centerY - radius / 2f), Offset(centerX, centerY - radius), Offset(centerX + triangleHeight, centerY - radius / 2f), Offset(centerX + triangleHeight, centerY + radius / 2f) ) // 计算每个顶点对应的圆角起止点 val cornerPoints = mutableListOf<Pair<Offset, Offset>>() for (i in points.indices) { val current = points[i] val prev = points[(i - 1 + points.size) % points.size] val next = points[(i + 1) % points.size] // 计算前一条边上的偏移点 val dirPrev = prev - current val dirPrevNormalized = dirPrev.normalize() val pointA = current + dirPrevNormalized * cornerRadius // 计算后一条边上的偏移点 val dirNext = next - current val dirNextNormalized = dirNext.normalize() val pointB = current + dirNextNormalized * cornerRadius cornerPoints.add(pointA to pointB) } // 初始化路径起点 moveTo(cornerPoints[0].first.x, cornerPoints[0].first.y) // 遍历绘制直线与圆弧 for (i in points.indices) { val nextA = cornerPoints[(i + 1) % points.size].first // 绘制两条圆角之间的直线段 lineTo(nextA.x, nextA.y) // 绘制当前顶点的圆弧 val center = points[i] val (currentA, currentB) = cornerPoints[i] val startAngle = (currentA - center).direction val endAngle = (currentB - center).direction arcTo( rect = Rect( left = center.x - cornerRadius, top = center.y - cornerRadius, right = center.x + cornerRadius, bottom = center.y + cornerRadius ), startAngleDegrees = startAngle, sweepAngleDegrees = endAngle - startAngle, forceMoveTo = false ) } close() } } // 扩展函数:归一化向量 private fun Offset.normalize(): Offset { val length = kotlin.math.sqrt(x * x + y * y) return if (length == 0f) Offset.Zero else Offset(x / length, y / length) } // 扩展函数:计算向量的方向角度(度) private fun Offset.direction(): Float { return Math.toDegrees(atan2(y.toDouble(), x.toDouble())).toFloat() }
调用Composable组件
@Composable fun Hexagon(modifier: Modifier = Modifier, cornerRadius: Dp = 8.dp) { val cornerRadiusPx = with(LocalDensity.current) { cornerRadius.toPx() } Box( modifier = modifier .clip(HexagonShape(cornerRadiusPx)) .background(md_theme_light_inversePrimary) .width(500.dp) .height(500.dp), contentAlignment = Alignment.Center ) { Image( painter = painterResource(id = R.drawable.app_bg), contentDescription = "My Hexagon Image", contentScale = ContentScale.Crop, modifier = Modifier .matchParentSize() .background(color = Color.Cyan) ) } }
关键说明
- 圆角半径可通过
cornerRadius参数自定义,建议不超过六边形边长的一半,避免路径异常 - 通过向量计算获取每条边上的偏移点,确保圆弧与六边形边平滑衔接
- 使用
arcTo绘制圆弧时,通过向量方向角度确定圆弧的起止角度,保证圆弧方向正确
内容的提问来源于stack exchange,提问作者Shodai Thox
相关产品推荐
相关产品推荐

