使用Jetpack Compose完成自定义手势处理,android开发技术难点

onGesture(必须):当拖动、缩放或旋转手势发生时回调

suspend fun PointerInputScope.detectTransformGestures(

panZoomLock: Boolean = false,

onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit

)

💡 Tips

关于偏移、缩放与旋转,我们建议的调用顺序是 rotate -> scale -> offset

  1. 若offset发生在rotate之前时,rotate会对offset造成影响。具体表现为当出现拖动手势时,组件会以当前角度为坐标轴进行偏移。
  1. 若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

}

)

}

)

}

}

transform

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 中发现了以下这些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值