跳到主要内容

Gesture 模块

viewcompose-gesture 是 ViewCompose 面向组合的手势 DSL。它向 Modifier 添加原始 Pointer、组合 点击、拖动、锚点拖动、Transform、优先级与嵌套滚动元素,并为 Renderer 回传提供可记忆的回调和 状态容器。该模块声明行为;Android Renderer 拥有原生指针流和识别引擎。

产物与稳定性

dependencies {
implementation("com.viewcompose:viewcompose-gesture:0.1.0-alpha04")
}
  • 稳定性:Alpha。Modifier 形态与当前状态语义已经审查并测试;手势仲裁和更完整的 Mutation API 在 Alpha 版本间仍可能演进。
  • 平台:Android Library API,但公开值保持 Renderer-neutral。
  • Gesture Core、Runtime、UI Contract 和 UI Foundation 会被传递暴露,因为它们的策略、State、 Modifier 和 Builder 类型出现在公开手势 API 中。
  • 大多数应用应依赖本产物,而不是直接依赖 Gesture Core。

识别所有权

手势 Modifier 是不可变描述。构建时不会安装 Android Listener,也不会自行识别输入。节点挂载后 由 Renderer 解释它们,并拥有 MotionEvent、Pointer ID、VelocityTracker、Touch Slop、布局 方向解析、识别器竞争、嵌套滚动顺序,以及替换或释放引起的取消。

回调在 Renderer 派发线程同步执行,Android 下一般是 UI 线程。回调应保持短小,把阻塞或挂起工作 交给应用拥有的协程。距离和 Pan 通常是物理像素;拖动终止速度是像素/秒。

Pointer 与点击输入

pointerInput 暴露归一化的 Down、Move、Up 与 Cancel 事件,不暴露原生事件对象。Key 是 Handler 的身份输入。仅在后续识别应视为已消费时返回 Consumed

combinedClickable 让一个 Renderer Recognizer 协调单击、双击与长按。计时和 Slop 来自 Android。禁用或没有任何回调时会原样返回 Modifier,不增加空闲原生识别器。

val actions = Modifier.combinedClickable(
onClick = { openItem() },
onLongClick = { openContextMenu() },
)

一维拖动

rememberDraggableState 返回稳定对象,同时始终转发给最新回调。它不累计或限制 Offset,规则由 应用状态拥有。draggable 声明方向和生命周期回调。识别跨过 Slop 并产生本地移动后才调用 Start; 正常 Stop 携带有符号的轴速度,取消使用独立回调。

Free 方向锁定到主轴。取消原因可能是系统取消、Transform 接管、Pointer 被消费、Modifier 替换 或释放。不能把取消当作零速度的正常 Stop。

锚点拖动

DraggableAnchors 是经过验证、不可变、非空的有限且唯一像素 Offset 集。输入顺序会自动排序。 语义值允许重复,但建议保持唯一,因为 offsetOf 选择第一个匹配;valueAt 使用 Float 精确相等。

val anchors = draggableAnchors<SheetValue> {
anchor(0f, SheetValue.Collapsed)
anchor(480f, SheetValue.Expanded)
}
val sheet = rememberAnchoredDraggableState(SheetValue.Collapsed)
val modifier = Modifier.anchoredDraggable(
state = sheet,
anchors = anchors,
orientation = GestureOrientation.Vertical,
)

Modifier 每次组合调用都会把最新 Anchors 同步安装进 State。当前值仍存在时,等价 Anchor 集合的 重新安装会保留正在拖动的 Offset;值消失时,最接近当前视觉 Offset 的 Anchor 成为当前值。原始 移动更新限制在已安装范围内的 Offset。正常结束会提交 Renderer 选择的最近目标,并可通过 onValueSettled 报告;取消会先恢复最后一次已提交 Anchor,再调用取消 Callback。当前版本立即 完成 Settle,没有动画,因此 targetValue 通常与 currentValue 同时变化。Q3 State 契约会为 每次 Delta、Snap、Settle、取消或 Anchor 对齐,通过一次 Snapshot Transaction 发布 Semantic Value、Target、Offset 与 Dragging 状态。Mutation 仍须串行运行在所属 UI 线程。

rememberAnchoredDraggableState 只在首次 Remember 时读取 initialValue。之后改变参数不会重置; 应显式调用 snapTo。Snap 到当前 Anchors 不包含的值会保存语义值并清除 Offset,直到下一次 Anchor 对齐。Anchored Drag 只接受 Horizontal 或 Vertical。

受控双状态拖动

rememberToggleDragStatetoggleDraggable 会把 Anchored Drag 适配为由调用方持有状态的 双状态组件,例如 Design System 自有的 Switch。Checked Anchor 是相对于 Unchecked 零点的有符号 物理像素偏移;对外的 progress 始终是从 0f Unchecked 到 1f Checked 的逻辑进度,因此 RTL 可以传入负数 Checked 偏移,而绘制逻辑不需要反转。

val drag = rememberToggleDragState(
checked = checked,
checkedAnchorOffsetPx = density.toPx(if (rtl) (-20).dp else 20.dp),
onCheckedChange = onCheckedChange,
)
val target = Modifier
.clickable { onCheckedChange(!checked) }
.toggleDraggable(drag)

移动尚未形成 Drag 时,Renderer 会把 Tap 留给 Click Modifier。已识别的 Drag 会消费结束事件,按 位置或速度 Settle,并只请求一次替换状态。组件继续拥有几何、Density/Layout Direction 转换、 Settled 动画、Checked Semantics 与持久化。拖动中使用 isDragging 直接绘制 Follow-finger 进度, 空闲时使用 Design System 自己的 Motion 契约。lastCompletion 会在替换状态 Callback 之前同步 发布,并保留正常 Settle 或取消恢复端点之前的逻辑进度。组件可用其中逐 State 递增的序列号与 起始进度继续 Settled 动画,避免短暂跳回旧端点。

Transform 手势

rememberTransformableState 转发增量乘法 Zoom、物理像素 Pan 和顺时针角度 Rotation。它不累计、 限制或动画化 Transform。transformable 提供 Start、正常 Stop 与 Cancel 回调。多指 Transform 激活可以接管并取消同一派发路径上的活跃 Drag。

应用状态应累计 Scale 和 Translation 并应用自身边界。避免保留逐事件对象,或每个 Transform Delta 都启动新协程。

手势优先级与嵌套滚动

gesturePriority(High) 请求更早的识别机会,但不保证消费。高优先级识别器拒绝事件后,其他 识别器仍可运行。

nestedScrollNestedScrollConnection 连接到已挂载的祖先链。Pre 回调在 Child 消费前, Post 回调在之后。可选 NestedScrollDispatcher 支持应用主动派发;未连接时消费零,Renderer 释放会断开旧 Connector,且不会干扰更新的 Mount。Connection 只能返回实际消费的距离或速度。

测试手势 UI

  • 将状态累计和 Anchor 替换与原生识别分开做单元测试。
  • 测试 Start、Delta、正常 Stop 与每种取消原因的回调顺序。
  • 在 Renderer 或设备测试中覆盖 Touch Slop、Drag/Transform 竞争和 RTL Swipe 方向。
  • 测试 Nested Scroll 的 Pre 由外到内、Post 由内到外,以及 Renderer 边界的过度消费限制。
  • 用新 Lambda 重组,验证 Remember 的 State 转发到最新回调。

模块测试覆盖 Modifier 编码、无操作点击声明、Drag 与 Transform 转发、Anchored 边界/重组/取消/Settle 行为、受控 LTR/RTL Toggle 进度、Settle 前 Toggle Completion 快照、 非法 Free 方向、优先级编码和 Nested Scroll 连接。

相关文档

完整生成参考位于 viewcompose-gesture API 树

兼容性说明

0.1.0-alpha04 建立同步回调传递、Latest-lambda Remember State、Renderer 所有识别、立即 Anchor Settle、显式取消和可断开的 Nested Scroll 派发。API 名称类似 Jetpack Compose Gesture Modifier 不代表具有相同的挂起 Mutation、MutatorMutex 或动画行为。