onGesture(必须):当拖动、缩放或旋转手势发生时回调
suspend fun PointerInputScope.detectTransformGestures(
panZoomLock: Boolean = false,
onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit
)
Tips
关于偏移、缩放与旋转,我们建议的调用顺序是 rotate -> scale -> offset
- 若offset发生在rotate之前时,rotate会对offset造成影响。具体表现为当出现拖动手势时,组件会以当前角度为坐标轴进行偏移。
- 若offset发生在scale之前是,scale也会对offset造成影响。具体表现为UI组件在拖动时不跟手
@Preview
@Composable
fun TransformGestureDemo() {
var boxSize = 100.dp
var offset by remember { mutableStateOf(Offset.Zero) }
var ratationAngle by remember { mutableStateOf(0f) }
var scale by remember { mutableStateOf(1f) }
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Box(Modifier
.size(boxSize)
.rotate(ratationAngle) // 需要注意offset与rotate的调用先后顺序
.scale(scale)
.offset {
IntOffset(offset.x.roundToInt(), offset.y.roundToInt())
}
.background(Color.Green)
.pointerInput(Unit) {
detectTransformGestures(
panZoomLock = true, // 平移或放大时是否可以旋转
onGesture = { centroid: Offset, pan: Offset, zoom: Float, rotation: Float ->
offset += pan
scale *= zoom
ratationAngle += rotation
}
)
}
)
}
}
forEachGesture
在传统 View 系统中,一次手指按下、移动到抬起过程中的所有手势事件可以共同构成一个手势事件序列。我们可以通过自定义手势处理来对于每一个手势事件序列进行定制处理。Compose 提供了 forEachGesture
以允许用户可以对每一个手势事件序列进行相同的定制处理。如果我们忘记使用 forEachGesture
,那么只会处理第一次手势事件序列。有些同学可能会问,为什么我不能在手势处理逻辑最外层套一层 while(true)
呢,通过 forEachGesture
的实现我们可以看到 forEachGesture
其实内部也是由while
实现的,除此之外他保证了协程只有存活时才能监听手势事件,同时也保证了每次交互结束时所有手指都是离开屏幕的。有些同学看到 while
可能新生疑问,难道这样不会阻塞主线程嘛?其实我们在介绍 PointerInput Modifier
时就提到过,我们的手势操作处理均发生在协程中。其实前面我们所提到的绝大多数 API 其内部实现均使用了 forEachGesture
。有些特殊场景下我们仅使用前面所提出的 API 可能仍然无法满足我们的需求,当然如果可以满足的话我们直接使用其分别对应的 Modifier
即可,前面所提出的 API 存在的意义是为了方便开发者为其进行功能拓展。既然要掌握自定义手势处理,我们就要从更底层角度来看这些上层 API 是如何实现的,了解原理我们就可以轻松自定义了。
suspend fun PointerInputScope.forEachGesture(block: suspend PointerInputScope.() -> Unit) {
val currentContext = currentCoroutineContext()
while (currentContext.isActive) {
try {
block()
// 挂起等待所有手指抬起
awaitAllPointersUp()
} catch (e: CancellationException) {
…
}
}
}
手势事件作用域 awaitPointerEventScope
在 PointerInputScope
中我们可以找到一个名为 awaitPointerEventScope
的 API 方法。
通过翻阅方法声明可以发现这是个挂起方法,其尾部 lambda 在 AwaitPointerEventScope
作用域中。 通过这个 AwaitPointerEventScope
作用域我们可以获取到更加底层的 API 手势事件,这也为自定义手势处理提供了可能。
suspend fun awaitPointerEventScope(
block: suspend AwaitPointerEventScope.() -> R
): R
我们在 AwaitPointerEventScope
中发现了以下这些基
文章评论