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

如何在Card顶部居中圆形图片下方绘制弧形?

不用Canvas的简洁解决方案

优先推荐自定义Shape的方式,完全适配Compose原生机制,能让Card边框和头像完美衔接,不会出现线条截断问题:

1. 自定义顶部内凹弧形Shape

创建一个自定义Shape类,在Card顶部中间挖一个和头像匹配的半圆弧形:

class TopCenterCutoutShape(private val cutoutRadius: Dp) : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val radius = with(density) { cutoutRadius.toPx() }
        val path = Path().apply {
            // 从左上角开始绘制轮廓
            moveTo(0f, radius)
            // 绘制左侧直线到弧形起点
            lineTo((size.width / 2) - radius, radius)
            // 绘制内凹半圆
            arcTo(
                rect = Rect(
                    left = (size.width / 2) - radius,
                    top = 0f,
                    right = (size.width / 2) + radius,
                    bottom = radius * 2
                ),
                startAngleDegrees = 180f,
                sweepAngleDegrees = 180f,
                forceMoveTo = false
            )
            // 绘制右侧直线到右下角
            lineTo(size.width, radius)
            lineTo(size.width, size.height)
            lineTo(0f, size.height)
            close()
        }
        return Outline.Generic(path)
    }
}

2. 修改Card和头像布局

把Card的默认Shape替换成自定义的,同时调整头像位置让它刚好卡在弧形里:

Box(modifier) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 50.dp),
        // 自定义Shape,cutoutRadius和头像半径一致(头像size100dp,所以设50dp)
        shape = TopCenterCutoutShape(50.dp),
        border = BorderStroke(1.dp, Color.LightGray)
    ) {
        // Card内部内容,注意加top padding避免被弧形挡住
        Box(modifier = Modifier.fillMaxSize().padding(top = 50.dp)) {
            // ... 原来的Card内部内容
        }
    }

    Box(
        modifier = Modifier
            .fillMaxWidth()
            // 调整offset让头像刚好嵌入弧形,减去border宽度避免错位
            .offset(y = -(50.dp - 1.dp))
            .zIndex(2f),
        contentAlignment = Alignment.TopCenter
    ) {
        Image(
            painter = painterResource(R.drawable.ic_android_black_24dp),
            contentDescription = "avatar",
            contentScale = ContentScale.Crop,
            modifier = Modifier
                .size(100.dp)
                .clip(CircleShape)
                .border(2.dp, Color.LightGray, CircleShape)
                .background(Color.White)
                .padding(16.dp)
        )
    }
}

备选临时方案(适合快速验证)

如果不想写自定义Shape,可以用同Card背景色的半圆遮挡Card边框:
在头像下方加一个只显示下半圆的Box,zIndex介于Card和头像之间:

Box(
    modifier = Modifier
        .fillMaxWidth()
        .offset(y = 15.dp)
        .zIndex(1f),
    contentAlignment = Alignment.TopCenter
) {
    Box(
        modifier = Modifier
            .size(100.dp)
            .clip(RoundedCornerShape(bottomStart = 50.dp, bottomEnd = 50.dp))
            .background(Color.White) // 颜色必须和Card背景一致
    )
}

这个方法缺点是依赖固定背景色,不如自定义Shape通用。

内容的提问来源于stack exchange,提问作者Gastón Saillán

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:52:09