android-jetpack-compose

Jetpack Compose: how to cut out card shape?


I want to create a half-transparent layer above the camera preview, like this:

enter image description here

I have the camera preview done in my app, all I want is a half-transparent layer over my preview with a cut-out card shape, like in the picture (with rounded corners).

So: fullscreen camera preview, on top of that there is a full screen half-transparent overlay, in which there is a card-shaped hole cut out

How can I do this?


Solution

  • You can use BlendModes to exclude a Rectangle from a transparent layer using

    @Composable
    private fun TransparentCamLayout() {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .drawWithContent {
    
                    val canvasWidth = size.width
                    val canvasHeight = size.height
                    val width = canvasWidth * .9f
                    val height = width * 3 / 4f
    
                    drawContent()
    
                    drawWithLayer {
    
                        // Destination
                        // This is transparent color
                        drawRect(Color(0x99000000))
    
                        // Source
                        // This is where we extract this rect from transparent
                        drawRect(
                            topLeft = Offset((canvasWidth - width) / 2, canvasHeight * .3f),
                            size = Size(width, height),
                            color = Color.Transparent,
                            blendMode = BlendMode.SrcIn
                        )
                    }
    
                    drawRect(
                        topLeft = Offset((canvasWidth - width) / 2, canvasHeight * .3f),
                        size = Size(width, height),
                        color = Color.White,
                        style = Stroke(2.dp.toPx())
                    )
                }
        ) {
            Image(
                modifier = Modifier.fillMaxSize(),
                painter = painterResource(id = R.drawable.landscape5),
                contentScale = ContentScale.Crop,
                contentDescription = null
            )
        }
    }
    
    /**
     * Draw with layer to use [BlendMode]s
     */
    private fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
        with(drawContext.canvas.nativeCanvas) {
            val checkPoint = saveLayer(null, null)
            block()
            restoreToCount(checkPoint)
        }
    }
    

    Result

    enter image description here

    In this tutorial's BlendMode section you can find other usages. As in this answer for custom clipping, building a rating bar and there are many usages limited with your imagination. Blend or PorterDuff modes are very functional for building custom clipping, alpha blending or pixel manipulation.