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

如何使用Jetpack Compose绘制两个圆形及指定形状?

Jetpack Compose 实现目标形状及绘制圆形

一、绘制两个圆形

在Compose里有两种常用的圆形实现方式:

1. 快速创建圆形View

通过Modifier.clip(CircleShape)配合背景色,就能快速生成圆形组件:

Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
    Box(
        modifier = Modifier
            .size(60.dp)
            .clip(CircleShape)
            .background(Color.Blue)
    )
    Box(
        modifier = Modifier
            .size(60.dp)
            .clip(CircleShape)
            .background(Color.Red)
    )
}

2. Canvas灵活绘制

如果需要自定义位置、边框等细节,用Canvas的drawCircle方法:

Canvas(modifier = Modifier.size(200.dp)) {
    // 填充圆形,中心在画布左半区
    drawCircle(
        color = Color.Blue,
        radius = 30.dp.toPx(),
        center = Offset(size.width / 4, size.height / 2)
    )
    // 带边框的圆形,中心在画布右半区
    drawCircle(
        color = Color.Red,
        radius = 30.dp.toPx(),
        center = Offset(size.width * 3 / 4, size.height / 2),
        style = Stroke(width = 2.dp.toPx())
    )
}

二、实现目标形状(参考配图)

从配图来看,该形状是底部为圆角矩形、顶部左右两侧向外突出半圆的自定义造型,以下两种方式可实现:

方法1:自定义可复用Shape

class CustomShape(private val cornerRadius: Dp, private val topCircleRadius: Dp) : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val cornerRadiusPx = cornerRadius.toPx(density)
        val topCircleRadiusPx = topCircleRadius.toPx(density)
        val path = Path().apply {
            // 从左下角圆角起点开始
            moveTo(cornerRadiusPx, size.height)
            // 绘制底部左侧圆角
            arcTo(
                rect = Rect(
                    left = 0f,
                    top = size.height - 2 * cornerRadiusPx,
                    right = 2 * cornerRadiusPx,
                    bottom = size.height
                ),
                startAngleDegrees = 90f,
                sweepAngleDegrees = 90f,
                forceMoveTo = false
            )
            // 绘制左侧边到顶部半圆起点
            lineTo(0f, topCircleRadiusPx)
            // 绘制左侧顶部外凸半圆
            arcTo(
                rect = Rect(
                    left = -topCircleRadiusPx,
                    top = 0f,
                    right = topCircleRadiusPx,
                    bottom = 2 * topCircleRadiusPx
                ),
                startAngleDegrees = 180f,
                sweepAngleDegrees = -180f,
                forceMoveTo = false
            )
            // 绘制顶部水平线到右侧半圆起点
            lineTo(size.width - topCircleRadiusPx, 0f)
            // 绘制右侧顶部外凸半圆
            arcTo(
                rect = Rect(
                    left = size.width - topCircleRadiusPx,
                    top = 0f,
                    right = size.width + topCircleRadiusPx,
                    bottom = 2 * topCircleRadiusPx
                ),
                startAngleDegrees = 0f,
                sweepAngleDegrees = -180f,
                forceMoveTo = false
            )
            // 绘制右侧边到右下角圆角起点
            lineTo(size.width, size.height - cornerRadiusPx)
            // 绘制底部右侧圆角
            arcTo(
                rect = Rect(
                    left = size.width - 2 * cornerRadiusPx,
                    top = size.height - 2 * cornerRadiusPx,
                    right = size.width,
                    bottom = size.height
                ),
                startAngleDegrees = 0f,
                sweepAngleDegrees = 90f,
                forceMoveTo = false
            )
            // 闭合路径
            close()
        }
        return Outline.Generic(path)
    }
}

使用该自定义Shape:

Box(
    modifier = Modifier
        .size(200.dp, 150.dp)
        .clip(CustomShape(cornerRadius = 16.dp, topCircleRadius = 24.dp))
        .background(Color.Green)
)

方法2:Canvas直接绘制路径

如果不需要复用,直接在Canvas中绘制:

Canvas(modifier = Modifier.size(200.dp, 150.dp)) {
    val cornerRadiusPx = 16.dp.toPx()
    val topCircleRadiusPx = 24.dp.toPx()
    val path = Path().apply {
        moveTo(cornerRadiusPx, size.height)
        arcTo(
            Rect(0f, size.height - 2 * cornerRadiusPx, 2 * cornerRadiusPx, size.height),
            90f, 90f, false
        )
        lineTo(0f, topCircleRadiusPx)
        arcTo(
            Rect(-topCircleRadiusPx, 0f, topCircleRadiusPx, 2 * topCircleRadiusPx),
            180f, -180f, false
        )
        lineTo(size.width - topCircleRadiusPx, 0f)
        arcTo(
            Rect(size.width - topCircleRadiusPx, 0f, size.width + topCircleRadiusPx, 2 * topCircleRadiusPx),
            0f, -180f, false
        )
        lineTo(size.width, size.height - cornerRadiusPx)
        arcTo(
            Rect(size.width - 2 * cornerRadiusPx, size.height - 2 * cornerRadiusPx, size.width, size.height),
            0f, 90f, false
        )
        close()
    }
    // 填充形状
    drawPath(path, color = Color.Green)
    // 可选:绘制边框
    drawPath(path, color = Color.Black, style = Stroke(2.dp.toPx()))
}

内容的提问来源于stack exchange,提问作者Null

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 01:31:08