Jetpack Compose实践:完成自定义手势处理

本文详细介绍了Jetpack Compose中自定义手势处理的实现,包括使用PointerInput Modifier,拖动、点击和变换类型的基础API,以及forEachGesture和awaitPointerEventScope的作用。通过示例展示了如何实现拖动、点击和变化手势,并讨论了事件分发流程和自定义事件处理。文章强调了理解和掌握自定义手势处理对于处理复杂手势需求的重要性。
摘要由CSDN通过智能技术生成

概述

Jetpack Compose 为我们提供了许多手势处理 Modifier,对于常见业务需求来说已足够我们使用了,然而如果说我们对手势有定制需求,就需要具备自定义手势处理的能力了。通过使用官方所提供的基础 API 来完成各类手势交互需求,触摸反馈基础 API 类似传统 View 系统的 onTouchEvent()。 当然 Compose 中也支持类似传统 ViewGroup 通过 onInterceptTouchEvent()定制手势事件分发流程。通过对自定义手势处理的学习将帮助大家掌握处理绝大多数场景下手势需求的能力。

使用 PointerInput Modifier

对于所有手势操作的处理都需要封装在这个 Modifier 中,我们知道 Modifier 是用来修饰 UI 组件的,所以将手势操作的处理封装在 Modifier 符合开发者设计直觉,这同时也做到了手势处理逻辑与 UI 视图的解耦,从而提高复用性。

通过翻阅 Swipeable ModifierDraggable Modifier 以及 Transformer Modifier,我们都能看到 PointerInput Modifier 的身影。因为这类上层的手势处理 Modifier 其实都是基于这个基础 Modifier 实现的。所以既然要自定义手势处理流程,自定义逻辑也必然要在这个 Modifier 中进行实现。

通过 PointerInput Modifier 实现我们可以看出,我们所定义的自定义手势处理流程均发生在 PointerInputScope 中,suspend 关键字也告知我们自定义手势处理流程是发生在协程中。这其实是无可厚非的,在探索重组工作原理的过程中我们也经常能够看到协程的身影。伴随着越来越多的主流开发技术拥抱协程,这也就意味着协程成了 Android 开发者未来必须掌握的技能。推广协程同时其实也是在推广 Kotlin,即使官方一直强调不会放弃 Java,然而谁又会在 Java 中使用 Kotlin 协程呢?

fun Modifier.pointerInput(
    vararg keys: Any?,
    block: suspend PointerInputScope.() -> Unit
): Modifier = composed(
    ...
) {
    ...
    remember(density) { SuspendingPointerInputFilter(viewConfiguration, density) }.apply {
        LaunchedEffect(this, *keys) {
            block()
        }
    }
}
复制代码

接下来我们就看看 PointerInputScope 作用域中,为我们可以使用哪些 API 来处理手势交互。本文将会根据手势能力分类进行解释说明。

拖动类型基础 API

API 介绍
API名称 作用
detectDragGestures 监听拖动手势
detectDragGesturesAfterLongPress 监听长按后的拖动手势
detectHorizontalDragGestures 监听水平拖动手势
detectVerticalDragGestures 监听垂直拖动手势

谈及拖动,许多人第一个反应就是 Draggable Modifier,因为 Draggable Modifier 为我们提供了监听 UI 组件拖动能力。然而 Draggable Modifier 在提供了监听 UI 组件拖动能力的同时也拓展增加其他功能,我们通过 Draggable Modifier 参数列表即可看出。例如通过使用 DraggableState 允许开发者根据需求使 UI 组件自动被拖动。

fun Modifier.draggable(
    state: DraggableState,
    orientation: Orientation,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource? = null,
    startDragImmediately: Boolean = false,
    onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
    onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {},
    reverseDirection: Boolean = false
)
复制代码

我们上面所罗列的这些拖动 API 只提供了监听 UI 组件拖动的能力,我们可以根据需求为其拓展功能,这也是这些API所存在的意义。我们从字面上就可以看出每个 API 所对应的含义,由于这些API的功能与参数相近,这里我们仅以 detectDragGestures 作为举例说明。

举例说明

接下来我们将完成一个绿色方块的手势拖动。在 Draggabel Modifier 中我们还只能监听垂直或水平中某一个方向的手势拖动,而使用 detectDragGestures 所有手势信息都是可以拿到的。如果我们还是只希望拿到某一个方向的手势拖动,使用 detectHorizontalDragGesturesdetectVerticalDragGestures 即可,当然我们也可以使用 detectDragGestures 并且忽略掉某个方向的手势信息。如果我们希望在长按后才能拿到手势信息可以使用 detectDragGesturesAfterLongPress

detectDragGestures 提供了四个参数。

onDragStart (可选):拖动开始时回调

onDragEnd (可选):拖动结束时回调

onDragCancel (可选):拖动取消时回调

onDrag (必须):拖动时回调

decectDragGestures 的源码分析在 awaitTouchSlopOrCancellation 小节会有讲解。

suspend fun PointerInputScope.detectDragGestures(
    onDragStart: (Offset) -> Unit = { },
    onDragEnd: () -> Unit = { },
    onDragCancel: () -> Unit = { },
    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
)
复制代码

💡 Tips

有些同学可能困惑 onDragCancel 触发时机。在一些场景中,当组件拖动时会根据事件分发顺序进行事件分发,当前面先处理事件的组件满足了设置的消费条件,导致手势事件被消费,导致本组件拿到的是被消费的手势事件,从而会执行 onDragCancel 回调。如何定制事件分发顺序并消费事件后续会进行详细的描述。

示例如下所示

@Preview
@Composable
fun DragGestureDemo() {
    var boxSize = 100.dp
    var offset by remember { mutableStateOf(Offset.Zero) }
    Box(contentAlignment = Alignment.Center,
        modifier = Modifier.fillMaxSize()
    ) {
        Box(Modifier
            .size(boxSize)
            .offset {
                IntOffset(offset.x.roundToInt(), offset.y.roundToInt())
            }
            .background(Color.Green)
            .pointerInput(Unit) {
                detectDragGestures(
                    onDragStart = { offset ->
                        // 拖动开始
                    },
                    onDragEnd = {
                        // 拖动结束
                    },
                    onDragCancel = {
                        // 拖动取消
                    },
                    onDrag = { change: PointerInputChange, dragAmount: Offset ->
                        // 拖动中
                        offset += dragAmount
                    }
                )
            }
        )
    }
}
复制代码

点击类型基础 API

API 介绍
API名称 作用
detectTapGestures 监听点击手势

Clickable Modifier 不同的是,detectTapGestures 可以监听更多的点击事件。作为手机监听的基础 API,必

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值