跳至主内容
版本:0.77

手势响应系统

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

手势响应系统负责管理应用中手势的生命周期。一次触摸操作可能会经历多个阶段,应用需要识别用户的操作意图。例如,应用需要判断触摸是滚动操作、滑动控件还是点击操作。甚至在触摸持续期间,操作类型也可能发生变化。同时可能存在多个并发的触摸操作。

触摸响应系统让组件能够协调这些触摸交互,而无需了解其父组件或子组件的任何信息。

最佳实践

要让应用体验出色,每个操作都应具备以下特性:

  • 反馈/高亮效果 - 向用户展示当前由哪个元素处理触摸,以及松开手势后会发生什么

  • 可取消性 - 用户在执行操作时,应能通过将手指拖离来中途取消操作

这些特性让用户使用应用时更加舒适,因为它允许人们自由尝试和交互,无需担心操作失误。

TouchableHighlight 与 Touchable* 组件

响应系统使用起来可能较为复杂。因此我们为需要"可点击"的元素提供了抽象的 Touchable 实现。它基于响应系统,允许您以声明式配置点击交互。在任何需要使用网页按钮或链接的地方,都可以使用 TouchableHighlight

响应者生命周期

视图可以通过实现正确的协商方法来成为触摸响应者。有两种方法用于询问视图是否希望成为响应者:

  • View.props.onStartShouldSetResponder: evt => true, - 该视图是否希望在触摸开始时成为响应者?

  • View.props.onMoveShouldSetResponder: evt => true, - 当视图不是响应者时,每次在视图上移动触摸都会调用:该视图是否希望"声明"触摸响应权?

如果视图返回 true 并尝试成为响应者,将发生以下情况之一:

  • View.props.onResponderGrant: evt => {} - 该视图现在负责响应触摸事件。此时应高亮显示并向用户展示当前状态

  • View.props.onResponderReject: evt => {} - 当前已有其他响应者且不会释放响应权

如果视图正在响应,可能会调用以下处理程序:

  • View.props.onResponderMove: evt => {} - 用户正在移动手指

  • View.props.onResponderRelease: evt => {} - 在触摸结束时触发,即"touchUp"事件

  • View.props.onResponderTerminationRequest: evt => true - 其他视图希望成为响应者。当前视图是否应释放响应权?返回 true 表示允许释放

  • View.props.onResponderTerminate: evt => {} - 响应权已从该视图移除。可能在调用 onResponderTerminationRequest 后被其他视图获取,也可能被操作系统不经询问直接获取(iOS 的控制中心/通知中心会出现这种情况)

evt 是一个合成触摸事件,结构如下:

  • nativeEvent
    • changedTouches - 自上次事件以来发生变化的触摸事件数组
    • identifier - 触摸标识符
    • locationX - 触摸相对于元素的 X 坐标
    • locationY - 触摸相对于元素的 Y 坐标
    • pageX - 触摸相对于根元素的 X 坐标
    • pageY - 触摸相对于根元素的 Y 坐标
    • target - 接收触摸事件的元素节点 ID
    • timestamp - 触摸时间标识符,用于速度计算
    • touches - 屏幕上当前所有触摸的数组

捕获 ShouldSet 处理程序

onStartShouldSetResponderonMoveShouldSetResponder 采用冒泡模式调用,即从最深层节点开始。这意味着当多个视图的 *ShouldSetResponder 处理程序返回 true 时,最深层组件将成为响应者。这在大多数情况下是符合预期的,因为它确保了所有控件和按钮的可操作性。

但有时父组件需要确保自己成为响应者。这时可通过捕获阶段实现。在响应系统从最深层组件开始冒泡之前,会先执行捕获阶段并触发 on*ShouldSetResponderCapture 方法。因此若父视图需要在触摸开始时阻止子组件成为响应者,应设置返回 true 的 onStartShouldSetResponderCapture 处理程序。

  • View.props.onStartShouldSetResponderCapture: evt => true,

  • View.props.onMoveShouldSetResponderCapture: evt => true,

PanResponder

如需进行更高级的手势解析,请参阅 PanResponder