Compose - Remove part of background of an image

98 Views Asked by At

I'd like to create a component with following result, assuming that the leaf is an existing ImageVector

enter image description here

With the following code:

@Composable
fun MyIcon(
    modifier: Modifier = Modifier,
) {

    Box(modifier = modifier) {
        Image(imageVector = leafImageVector)
        Ring( // the part that need to be removed from Image
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(
                    end = 4.dp,
                    bottom = 8.dp
                ),
        )
        Badge(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(
                    end = 8.dp,
                    bottom = 8.dp
                ),
            badgeColor = badgeColor
        )
    }
}

@Composable
private fun Ring(
    modifier: Modifier = Modifier,
) {

    Canvas(
        modifier = modifier.size(8.dp),
        onDraw = {

            val strokeWidth = 8f
            drawCircle(
                color = Color.Black, // Change this to Transparent or Unspecified does not help
                radius = 12f,
                style = Stroke(
                    width = strokeWidth
                )
            )

        }
    )
}

@Composable
fun Badge(
    modifier: Modifier = Modifier,
    badgeColor: Color,
) {
    Box(
        modifier = modifier
            .size(8.dp)
            .clip(CircleShape)
            .background(
                badgeColor
            )
    )
}

The black part is the part I need to be removed from the Image

1

There are 1 best solutions below

1
Thracian On BEST ANSWER

You can use BlendMods inside Modifier.drawWithContent to remove some part of Composable, it doesn't necessarily need to be Image for this to work. You can clip or cut from any Composable.

How to clip or cut a Composable?

enter image description here

@Preview
@Composable
fun ImageEraseTest() {
    Column(
        modifier = Modifier.fillMaxSize().padding(32.dp)
    ) {
        Image(
            modifier = Modifier
                .size(80.dp)
                .border(2.dp, Color.Red)
                .graphicsLayer {
                    compositingStrategy = CompositingStrategy.Offscreen
                }
                .drawWithContent {
                    val radius = 16.dp.toPx()
                    val badgeRadius = 12.dp.toPx()

                    val center = Offset(
                        size.width - radius ,
                        size.height - radius
                    )
                    
                    // Destination
                    drawContent()
                    
                    // Source
                    drawCircle(
                        color = Color.Transparent,
                        radius = radius,
                        center = center,
                        blendMode = BlendMode.Clear
                    )

                    drawCircle(
                        color = Color.Red,
                        radius = badgeRadius,
                        center = center
                    )
                },
            imageVector = Icons.Default.AccountCircle,
            contentDescription = null
        )
    }
}

You can also draw red circle if you wish to as in example above.