android 如果我的圆形图像的顶部位于一张牌的中心,我如何在它的下面画一个弧?

bwitn5fc  于 2023-11-15  发布在  Android
关注(0)|答案(1)|浏览(150)

我正在寻找如何添加这个弧在我的卡的中间,但我不能找到一个干净的方法来做


的数据
我现在的代码是

Box(modifier) {
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 50.dp),
            shape = RoundedCornerShape(8),
            border = BorderStroke(1.dp, Color.LightGray)
        ) {
            ...

            }
        }
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .offset(y = (15).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)

            )
        }
    }

字符串
但目前我的输出是



有没有一种简洁的方法来画出这条曲线?
我试着用剪辑,和相同颜色的背景,但线突然削减时,达到圆形图像。
我从来没有使用画布之前,它可能是解决方案,但我需要知道是否有另一种方式这样做
谢谢你,谢谢

svgewumm

svgewumm1#


的数据
你可以像这样创建这个形状。你可以改变系数来进一步定制它

fun Path.roundedRectanglePath(
    size: Size,
    cornerRadius: Float,
    fabRadius: Float,
) {

    val horizontalCoefficient = 1.09f
    val verticalCoefficient = 0.4f
    val centerX = size.width / 2
    val x0 = centerX - fabRadius * horizontalCoefficient
    val y0 = 0f

    // offset of the first control point (top part)
    val topControlX = x0 + fabRadius * verticalCoefficient
    val topControlY = y0

    // offset of the second control point (bottom part)
    val bottomControlX = x0
    val bottomControlY = y0 + fabRadius

    // first curve
    // set the starting point of the curve (P2)
    val firstCurveStart = Offset(x0, y0)

    // set the end point for the first curve (P3)
    val firstCurveEnd = Offset(centerX, fabRadius)

    // set the first control point (C1)
    val firstCurveControlPoint1 = Offset(
        x = topControlX,
        y = topControlY
    )

    // set the second control point (C2)
    val firstCurveControlPoint2 = Offset(
        x = bottomControlX,
        y = bottomControlY
    )

    // second curve
    // end of first curve and start of second curve is the same (P3)
    val secondCurveStart = Offset(
        x = firstCurveEnd.x,
        y = firstCurveEnd.y
    )

    // end of the second curve (P4)
    val secondCurveEnd = Offset(
        x = centerX + fabRadius * horizontalCoefficient,
        y = 0f
    )

    // set the first control point of second curve (C4)
    val secondCurveControlPoint1 = Offset(
        x = secondCurveStart.x + fabRadius,
        y = bottomControlY
    )

    // set the second control point (C3)
    val secondCurveControlPoint2 = Offset(
        x = secondCurveEnd.x - fabRadius * verticalCoefficient,
        y = topControlY
    )

    // Top left arc
    val radius = cornerRadius * 2

    arcTo(
        rect = Rect(
            left = 0f,
            top = 0f,
            right = radius,
            bottom = radius
        ),
        startAngleDegrees = 180.0f,
        sweepAngleDegrees = 90.0f,
        forceMoveTo = false
    )

    lineTo(x = firstCurveStart.x, y = firstCurveStart.y)

    // bezier curve with (P2, C1, C2, P3)
    cubicTo(
        x1 = firstCurveControlPoint1.x,
        y1 = firstCurveControlPoint1.y,
        x2 = firstCurveControlPoint2.x,
        y2 = firstCurveControlPoint2.y,
        x3 = firstCurveEnd.x,
        y3 = firstCurveEnd.y
    )

    // bezier curve with (P3, C4, C3, P4)
    cubicTo(
        x1 = secondCurveControlPoint1.x,
        y1 = secondCurveControlPoint1.y,
        x2 = secondCurveControlPoint2.x,
        y2 = secondCurveControlPoint2.y,
        x3 = secondCurveEnd.x,
        y3 = secondCurveEnd.y
    )

    lineTo(x = size.width - cornerRadius, y = 0f)

    // Top right arc
    arcTo(
        rect = Rect(
            left = size.width - radius,
            top = 0f,
            right = size.width,
            bottom = radius
        ),
        startAngleDegrees = -90.0f,
        sweepAngleDegrees = 90.0f,
        forceMoveTo = false
    )

    lineTo(x = 0f + size.width, y = size.height - cornerRadius)

    // Bottom right arc
    arcTo(
        rect = Rect(
            left = size.width - radius,
            top = size.height - radius,
            right = size.width,
            bottom = size.height
        ),
        startAngleDegrees = 0f,
        sweepAngleDegrees = 90.0f,
        forceMoveTo = false
    )

    lineTo(x = cornerRadius, y = size.height)

    // Bottom left arc
    arcTo(
        rect = Rect(
            left = 0f,
            top = size.height - radius,
            right = radius,
            bottom = size.height
        ),
        startAngleDegrees = 90.0f,
        sweepAngleDegrees = 90.0f,
        forceMoveTo = false
    )

    lineTo(x = 0f, y = cornerRadius)
    close()
}

字符串
然后创建一个使用此形状的Composable。其中的值也可以自定义。在此示例中使用了shadow,但它可以轻松地替换为M odifier.border(width, color, shape)

@Composable
private fun CustomArcShape(
    modifier: Modifier,
    elevation: Dp = 4.dp,
    color: Color = MaterialTheme.colors.surface,
    contentColor: Color = contentColorFor(color),
    content: @Composable () -> Unit
) {

    Column {
        val diameter = 80.dp
        val radiusDp = diameter / 2

        val cornerRadiusDp = 10.dp

        val density = LocalDensity.current
        val cutoutRadius = density.run { radiusDp.toPx() * 1.5f }
        val cornerRadius = density.run { cornerRadiusDp.toPx() }

        val shape = remember {
            GenericShape { size: Size, layoutDirection: LayoutDirection ->
                this.roundedRectanglePath(
                    size = size,
                    cornerRadius = cornerRadius,
                    fabRadius = cutoutRadius
                )
            }
        }

        Spacer(modifier = Modifier.height(diameter / 2))

        Box(contentAlignment = Alignment.TopCenter) {

            Icon(
                modifier = Modifier
                    .offset(y = -diameter / 4)
                    .background(Green400, CircleShape)
                    .size(diameter)
                    .align(Alignment.TopCenter)
                    .padding(16.dp),
                tint = Color.Black,
                imageVector = Icons.Filled.ThumbUp,
                contentDescription = "ThumbUp"
            )

            Surface(
                modifier = modifier,
                shape = shape,
                elevation = elevation,
                color = color,
                contentColor = contentColor
            ) {
                Column {
                    Spacer(modifier = Modifier.height(diameter))
                    content()
                }
            }
        }
    }
}

编辑

与边境


编辑2

用一个有边框和阴影的盒子,没有任何背景来显示形状。


@Preview
@Composable
private fun Test() {
    MyComposeScreen(
        modifier = Modifier.padding(16.dp).fillMaxSize(),
        onActionClick = {},
        onGoToHomeClick = {}
    )
}

@Composable
fun MyComposeScreen(
    modifier: Modifier,
    onActionClick: () -> Unit,
    onGoToHomeClick: () -> Unit
) {
    val diameter = 80.dp
    val radiusDp = diameter / 2.3f

    val cornerRadiusDp = 20.dp

    val density = LocalDensity.current
    val cutoutRadius = density.run { radiusDp.toPx() * 1.5f }
    val cornerRadius = density.run { cornerRadiusDp.toPx() }

    val shape = remember {
        GenericShape { size: Size, layoutDirection: LayoutDirection ->
            this.roundedRectanglePath(
                size = size,
                cornerRadius = cornerRadius,
                cutoutRadius = cutoutRadius
            )
        }
    }

    Box(modifier) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 95.dp)
                .shadow(1.dp, shape, spotColor = Color.Red)
                .border(1.dp, Color.LightGray,shape),

            ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    modifier = Modifier.padding(top = 57.dp, bottom = 16.dp),
                    text = "test",
                    textAlign = TextAlign.Center
                )
                Divider(
                    modifier = Modifier
                        .height(1.dp)
                        .background(Color.LightGray.copy(alpha = .1f))
                )
                Text(
                    modifier = Modifier.padding(top = 16.dp),
                    text = "test",
                )
            }
        }
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .offset(y = diameter - 20.dp),
            contentAlignment = Alignment.TopCenter
        ) {
            Image(
                painter = painterResource(R.drawable.ic_launcher_background),
                contentDescription = "avatar",
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .size(diameter)
                    .clip(CircleShape)
                    .background(Color.Green.copy(alpha = .5f))
                    .padding(16.dp),
                colorFilter = ColorFilter.tint(Color.White)

            )
        }
    }
}

相关问题