如何使用触摸事件在Jetpack Compose Canvas上进行绘制?

9
这是一个关于使用Jetpack Canvas绘图样例的问答式问题,我在stackoverflow上找到了这个或者另外一个相关问题。发现可以使用pointerInteropFilter来绘制类似View的onTouchEventMotionEvent的内容,但是文档不建议这么做。请注意保留HTML标签。
一个特殊的PointerInputModifier,提供对最初分派到Compose的MotionEvents的访问。首选pointerInput,并仅在与消耗MotionEvents的现有代码进行交互时使用此功能。虽然该修改器的主要目的是允许任意代码访问分派给Compose的原始MotionEvent,但为了完整起见,提供了类似物来允许任意代码与系统进行交互,就像它是Android View一样。
1个回答

17

编辑

我在发布这个答案后已经有一段时间了,并且从这个问题的反馈中得知,之前的答案对于初学者来说有点令人困惑,因此我将其简化了。关于手势等的库现在可以在Github存储库中找到。

和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() 用于防止在画布外部绘制。

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存储库中,这里有一个详细的解释关于手势,如果您不想使用现有的手势,您可以很容易地构建自己的手势。在视图的onTouchEvent第一次触摸后会出现延迟,我的设备上大约为16毫秒,这是我测得的最快时间,我也将其添加到了Compose的手势中,因为当用户初始指针移动非常迅速时,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 存储库也可在此获取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接