android - 如何使用触摸事件在 Jetpack Compose Canvas 上绘图?

标签 android android-jetpack-compose android-jetpack-compose-canvas

这是Q&A-style问题,因为我正在寻找带有 Jetpack Canvas 的绘图样本,但有关 stackoverflow 的问题,this oneanother one ,我发现使用 pointerInteropFilter用于像 View 的 onTouchEvent 进行绘图MotionEvent s 根据文档不建议为

A special PointerInputModifier that provides access to the underlying MotionEvents originally dispatched to Compose. Prefer pointerInput and use this only for interoperation with existing code that consumes MotionEvents.

While the main intent of this Modifier is to allow arbitrary code to access the original MotionEvent dispatched to Compose, for completeness, analogs are provided to allow arbitrary code to interact with the system as if it were an Android View.

最佳答案

编辑
自从我发布此答案以来已经有一段时间了,我收到了来自 this question 的反馈以前的答案对初学者来说有点困惑,所以我简化了它,这个手势的库和更多可用在 github repo .
我们需要运动状态,就像 View 的第一个

enum class MotionEvent {
    Idle, Down, Move, Up
}
需要空闲状态才能在 Up 状态下保持状态,因为如果发生任何重组,您的 Canvas 会以 Up 状态重组,这会导致不需要的绘图甚至崩溃。
路径、当前触摸位置和触摸状态
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) }
previousPosition是可选的,我使用它是因为我想用 path.quadraticBezierTo 绘制平滑的线条, 而不是 path.lineTo 在使用指针移动时
用于创建触摸事件的修饰符。 Modifier.clipToBounds()是为了防止在 Canvas 之外绘图。
val drawModifier = Modifier
    .fillMaxWidth()
    .height(300.dp)
    .clipToBounds()
    .background(Color.White)
    .pointerMotionEvents(
        onDown = { pointerInputChange: PointerInputChange ->
            currentPosition = pointerInputChange.position
            motionEvent = MotionEvent.Down
            pointerInputChange.consume()
        },
        onMove = { pointerInputChange: PointerInputChange ->
            currentPosition = pointerInputChange.position
            motionEvent = MotionEvent.Move
            pointerInputChange.consume()
        },
        onUp = { pointerInputChange: PointerInputChange ->
            motionEvent = MotionEvent.Up
            pointerInputChange.consume()
        },
        delayAfterDownInMillis = 25L
    )
Modifier.pointerMotionEvents我为它编写的自定义手势库是 onTouchEvent 的对应物,它可以在上面的 github repo 上找到,这里是 detailed explanation关于手势,如果您不想这样做,您可以轻松构建自己的手势。 View 的 onTouchEvent 上发生第一次触摸后的延迟,在我的设备上大约为 16 毫秒,这是我测量的最快的,我也在 Compose 上添加了手势,因为当用户最初快速移动指针时,Canvas 无法处理向下事件。
并将此修改器应用于 Canvas 并根据当前状态和位置移动或绘制
Canvas(modifier = drawModifier) {


    when (motionEvent) {
        MotionEvent.Down -> {
            path.moveTo(currentPosition.x, currentPosition.y)
            previousPosition = currentPosition
        }

        MotionEvent.Move -> {
            path.quadraticBezierTo(
                previousPosition.x,
                previousPosition.y,
                (previousPosition.x + currentPosition.x) / 2,
                (previousPosition.y + currentPosition.y) / 2

            )
            previousPosition = currentPosition
        }

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

        else -> Unit
    }

    drawPath(
        color = Color.Red,
        path = path,
        style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
    )
}
enter image description here
完整绘图应用程序的 Github 存储库也可用 here .

关于android - 如何使用触摸事件在 Jetpack Compose Canvas 上绘图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71090111/

相关文章:

java - 如何检查firebase recylerview在android中是否有值或者为空?

Android - 工具栏的标准高度

android - 将底部导航栏与 Jetpack Compose 结合使用的正确策略

android - 如何在 Jetpack Compose 中将对象的移动从一个路径平滑地切换到另一路径?

android - 无法在 Jetpack Compose 中找到路径的 minus()

android - 伪元素:after disapears on chrome mobile

android - 如何将 CameraView 与 Jetpack Compose 一起使用?

android - Jetpack Compose 中的密集文本字段

android - Android 中的 SQLiteDiskIOException