简体   繁体   English

如何在 jetpack compose 中实现以下布局?

[英]How can I achieve the below layout in jetpack compose?

This layout is made by me, the layout you are looking is a SVG image so I have just made the image to fill max size and added the above text and camera capture button below.此布局由我制作,您正在查看的布局是 SVG 图像,所以我刚刚制作了图像以填充最大尺寸,并在下面添加了上面的文本和相机捕捉按钮。 But now I want to remove the image background and want to make the same layout programmatically.但现在我想删除图像背景并想以编程方式制作相同的布局。

在此处输入图像描述

Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.fillMaxSize()) {
    AndroidView({ previewView }, modifier = Modifier.fillMaxSize())

    Column(modifier = Modifier.fillMaxSize()) {

        Icon(
            painter = painterResource(id = R.drawable.ic_card_overlay),
            contentDescription = null
        )

        Image(
            modifier = Modifier.fillMaxSize(),
            painter = painterResource(id = R.drawable.ic_black_transparent),
            contentDescription = null,
            contentScale = ContentScale.FillWidth
        )
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(26.dp)
    ) {

        Row(
            modifier = Modifier
                .padding(bottom = 20.dp), verticalAlignment = Alignment.CenterVertically
        ) {

            Icon(
                modifier = Modifier.clickable {
                    onCloseCameraClick()
                },
                painter = painterResource(id = R.drawable.ic_baseline_arrow_back_ios_24),
                contentDescription = null,
                tint = Color.White
            )

            Text(
                text = "Passport",
                color = Color.White,
                fontSize = 20.sp
            )
        }

        Text(
            text = "Place your passport inside the frame and take a\npicture.\nMake sure it is not cut or has any glare.",
            color = Color.White,
            fontSize = 12.sp
        )
    }

    IconButton(
        modifier = Modifier.padding(bottom = 20.dp),
        onClick = {
            Log.d("takePhoto", "ON CLICK")
            takePhoto(
                imageCapture = imageCapture,
                outputDirectory = outputDirectory,
                executor = executor,
                onImageCaptured = onImageCaptured,
                onError = onError
            )
        },
        content = {
            Icon(
                painter = painterResource(id = R.drawable.ic_baseline_camera_24),
                contentDescription = stringResource(R.string.take_picture),
                tint = Color.White,
                modifier = Modifier
                    .fillMaxSize(0.2f)
            )
        }
    )
}

You can see I have used ic_card_overlay image which act like a background.你可以看到我使用了像背景一样的 ic_card_overlay 图像。 I want to achieve the same black transparent background with the box in the middle which will not include the black transparent color.我想用中间的框实现相同的黑色透明背景,其中不包括黑色透明色。 Thank you.谢谢你。

You can achieve this with using BlendMode.Clear您可以使用 BlendMode.Clear 来实现这一点

@Composable
fun TransparentClipLayout(
    modifier: Modifier,
    width: Dp,
    height: Dp,
    offsetY: Dp
) {

    val offsetInPx: Float
    val widthInPx: Float
    val heightInPx: Float

    with(LocalDensity.current) {
        offsetInPx = offsetY.toPx()
        widthInPx = width.toPx()
        heightInPx = height.toPx()
    }

    Canvas(modifier = modifier) {

        val canvasWidth = size.width

        with(drawContext.canvas.nativeCanvas) {
            val checkPoint = saveLayer(null, null)

            // Destination
            drawRect(Color(0x77000000))

            // Source
            drawRoundRect(
                topLeft = Offset(
                    x = (canvasWidth - widthInPx) / 2,
                    y = offsetInPx
                ),
                size = Size(widthInPx, heightInPx),
                cornerRadius = CornerRadius(30f,30f),
                color = Color.Transparent,
                blendMode = BlendMode.Clear
            )
            restoreToCount(checkPoint)
        }

    }
}

You can customize corner radius size too.您也可以自定义角半径大小。 This is only for demonstration这仅用于演示

Usage用法

Column {

    Box(modifier = Modifier.fillMaxSize()) {
        Image(
            modifier =Modifier.fillMaxSize(),
            painter = painterResource(id = R.drawable.landscape1),
            contentDescription = null,
            contentScale = ContentScale.Crop
        )
        TransparentClipLayout(
            modifier = Modifier.fillMaxSize(),
            width = 300.dp,
            height = 200.dp,
            offsetY = 150.dp
        )
    }
}

Result结果

在此处输入图像描述

You can archieve this background layout using a custom Shape in combination with a Surface .您可以将自定义ShapeSurface结合使用来归档此背景布局。 With a custom implementation you can define what parts of the Surface are displayed and which parts are "cut out".通过自定义实现,您可以定义Surface的哪些部分被显示以及哪些部分被“剪切”。

The cutoutPath defines the part which are highlighted. cutoutPath定义了突出显示的部分。 Here it is defined as a RoundRect with a dynamically calculated position and size.在这里,它被定义为具有动态计算的 position 和大小的RoundRect Adjust the topLeft and ``formulas as you need.根据需要调整topLeft和 ``公式。

Using Path.combine(...) the outlinePath is combined with the cutoutPath .使用Path.combine(...)outlinePathcutoutPath相结合。 This is where the magic happens.这就是魔法发生的地方。

/**
 * This is a shape with cuts out a rectangle in the center
 */
class CutOutShape : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val outlinePath = Path()
        outlinePath.addRect(Rect(Offset(0f, 0f), size))

        val cutoutHeight = size.height * 0.3f
        val cutoutWidth = size.width * 0.75f
        val center = Offset(size.width / 2f, size.height / 2f)

        val cutoutPath = Path()
        cutoutPath.addRoundRect(
            RoundRect(
                Rect(
                    topLeft = center - Offset(
                        cutoutWidth / 2f,
                        cutoutHeight / 2f
                    ),
                    bottomRight = center + Offset(
                        cutoutWidth / 2f,
                        cutoutHeight / 2f
                    )
                ),
                cornerRadius = CornerRadius(16f, 16f)
            )
        )

        val finalPath = Path.combine(
            PathOperation.Difference,
            outlinePath,
            cutoutPath
        )

        return Outline.Generic(finalPath)
    }
}

The shape can be used like this:形状可以这样使用:

Surface(
    shape = CutOutShape(),
    color = Color.Black.copy(alpha = 0.45f)
) { }

This results in the following screen:这将导致以下屏幕:

在此处输入图像描述

Box {
    AndroidView({ previewView }, modifier = Modifier.fillMaxSize())

    Surface(
        shape = CutOutShape(),
        color = Color.Black.copy(alpha = 0.45f),
        modifier = Modifier.fillMaxSize()
    ) { }

    Column(
        modifier = Modifier
            .padding(top = 54.dp, start = 32.dp, end = 32.dp, bottom = 54.dp)
    ) {
        Row(
            Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            IconButton(onClick = { /*TODO*/ }) {
                Icon(
                    imageVector = Icons.TwoTone.ArrowBack,
                    contentDescription = null,
                    tint = Color.White
                )
            }

            Text(
                "Passport",
                color = Color.White,
                fontSize = 20.sp
            )
        }

        Text(
            "Place your passport inside the frame and take a picture.\nMake sure it is not cut or has any glare.",
            color = Color.White,
            fontSize = 12.sp
        )

        Spacer(modifier = Modifier.weight(1f))

        Icon(
            imageVector = Icons.TwoTone.Camera,
            contentDescription = null,
            tint = Color.White,
            modifier = Modifier
                .size(48.dp)
                .align(Alignment.CenterHorizontally)
        )
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM