编辑
我在发布这个答案后已经有一段时间了,并且从这个问题的反馈中得知,之前的答案对于初学者来说有点令人困惑,因此我将其简化了。关于手势等的库现在可以在Github存储库中找到。
和View一样,我们需要运动状态。
enum class MotionEvent {
Idle, Down, Move, Up
}
需要保持空闲状态以避免在 Up 状态下离开状态,因为如果发生任何重新组合,您的 Canvas 会与 Up 状态一起重新组合,从而导致不必要的绘画甚至崩溃。
路径、当前触摸位置和触摸状态
var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
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](https://istack.dev59.com/E49QZ.gif)
完整的绘图应用程序的 Github 存储库也可在此处获取。