You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:10:14