android - 如何在 Jetpack Compose 中删除 Canvas 时变得透明,现在我变成了白色?

标签 android kotlin canvas android-jetpack-compose transparent

如何使 Canvas 的某些部分透明?我希望用户能够像这样删除照片的一部分 link显示为透明。我的 Canvas 代码:

Canvas(
    modifier = modifier
        .background(Color.Transparent)
) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        drawImage(
            image = bitmap,
            srcSize = IntSize(bitmap.width, bitmap.height),
            dstSize = IntSize(canvasWidth, canvasHeight)
        )
        drawPath(
            path = erasePath,
            style = Stroke(
                width = 30f,
                cap = StrokeCap.Round,
                join = StrokeJoin.Round
            ),
            blendMode = BlendMode.Clear,
            color = Color.Transparent,
        )
        restoreToCount(checkPoint)
    }
}

最佳答案

你得到的透明是 Color(0x00000000),你得到的白色是你背景的颜色,即使你的 Canvas 有透明背景,你的根或父 Composable 的颜色是白色。

您需要先绘制棋盘格布局或棋盘格图像,在层内您应该使用 BlendMode.Clear 绘制图像和路径

val width = this.size.width
val height = this.size.height

val checkerWidth = 10.dp.toPx()
val checkerHeight = 10.dp.toPx()

val horizontalSteps = (width / checkerWidth).toInt()
val verticalSteps = (height / checkerHeight).toInt()

for (y in 0..verticalSteps) {
    for (x in 0..horizontalSteps) {
        val isGrayTile = ((x + y) % 2 == 1)
        drawRect(
            color = if (isGrayTile) Color.LightGray else Color.White,
            topLeft = Offset(x * checkerWidth, y * checkerHeight),
            size = Size(checkerWidth, checkerHeight)
        )
    }
}

val space = 20.dp.roundToPx()

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

    // Destination
    drawImage(
        image = dstBitmap,
        dstOffset = IntOffset(
            space / 2,
            space / 2
        ),
        dstSize = IntSize(
            canvasWidth - space, canvasHeight - space
        )
    )

    // Source
    drawPath(
        color = Color.Transparent,
        path = erasePath,
        style = Stroke(
            width = 30f,
            cap = StrokeCap.Round,
            join = StrokeJoin.Round
        ),
        blendMode = BlendMode.Clear
    )
    restoreToCount(checkPoint)
}

完整实现

@Composable
private fun MyImageDrawer(modifier: Modifier) {

    // This is the image to draw onto
    val dstBitmap = ImageBitmap.imageResource(id = R.drawable.landscape1)


    // Path used for erasing. In this example erasing is faked by drawing with canvas color
    // above draw path.
    val erasePath = remember { Path() }

    var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
    // This is our motion event we get from touch motion
    var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
    // This is previous motion event before next touch is saved into this current position
    var previousPosition by remember { mutableStateOf(Offset.Unspecified) }


    val drawModifier = modifier
        .pointerMotionEvents(Unit,
            onDown = { pointerInputChange ->
                motionEvent = MotionEvent.Down
                currentPosition = pointerInputChange.position
                pointerInputChange.consume()
            },
            onMove = { pointerInputChange ->
                motionEvent = MotionEvent.Move
                currentPosition = pointerInputChange.position
                pointerInputChange.consume()
            },
            onUp = { pointerInputChange ->
                motionEvent = MotionEvent.Up
                pointerInputChange.consume()
            }
        )

    Canvas(modifier = drawModifier) {

        val canvasWidth = size.width.roundToInt()
        val canvasHeight = size.height.roundToInt()

        // Draw or erase depending on erase mode is active or not

        when (motionEvent) {

            MotionEvent.Down -> {
                erasePath.moveTo(currentPosition.x, currentPosition.y)
                previousPosition = currentPosition

            }
            MotionEvent.Move -> {

                erasePath.quadraticBezierTo(
                    previousPosition.x,
                    previousPosition.y,
                    (previousPosition.x + currentPosition.x) / 2,
                    (previousPosition.y + currentPosition.y) / 2

                )
                previousPosition = currentPosition
            }

            MotionEvent.Up -> {
                erasePath.lineTo(currentPosition.x, currentPosition.y)
                currentPosition = Offset.Unspecified
                previousPosition = currentPosition
                motionEvent = MotionEvent.Idle
            }
            else -> Unit
        }


        val width = this.size.width
        val height = this.size.height

        val checkerWidth = 10.dp.toPx()

        val checkerHeight = 10.dp.toPx()

        val horizontalSteps = (width / checkerWidth).toInt()
        val verticalSteps = (height / checkerHeight).toInt()

        for (y in 0..verticalSteps) {
            for (x in 0..horizontalSteps) {
                val isGrayTile = ((x + y) % 2 == 1)
                drawRect(
                    color = if (isGrayTile) Color.LightGray else Color.White,
                    topLeft = Offset(x * checkerWidth, y * checkerHeight),
                    size = Size(checkerWidth, checkerHeight)
                )
            }
        }

        val space = 20.dp.roundToPx()

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

            // Destination
            drawImage(
                image = dstBitmap,
                dstOffset = IntOffset(
                    space / 2,
                    space / 2
                ),
                dstSize = IntSize(
                    canvasWidth - space, canvasHeight - space
                )
            )

            // Source
            drawPath(
                color = Color.Transparent,
                path = erasePath,
                style = Stroke(
                    width = 30f,
                    cap = StrokeCap.Round,
                    join = StrokeJoin.Round
                ),
                blendMode = BlendMode.Clear
            )
            restoreToCount(checkPoint)
        }
    }
}

结果

enter image description here

关于android - 如何在 Jetpack Compose 中删除 Canvas 时变得透明,现在我变成了白色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73023593/

相关文章:

android - 在 onBindViewHolder 之外使用 ViewHolder 元素?

Kotlin 在最后一个空格分割字符串

arrays - Kotlin 数组类型和类字面量

javascript - 无法在 Canvas 上绘制同一图像的多个副本?

javascript - 尝试镜像 Canvas 上下文无法渲染 Canvas

android - 无法在 Android Studio 项目中引用导入的 jar 库

java - 如何从 GCM 的 API key 获取项目 ID?

java - 如何使用 Java GCM API 在 Android 设备上获取失败推送通知的注册 ID

android - Android AVD只能一次启动

javascript - 如何将同一对象的多个图像绘制到单个 Canvas 上