手势响应器系统
手势响应器系统管理应用中手势的生命周期。触摸在应用确定用户意图的过程中会经历几个阶段。例如,应用需要确定触摸是滚动、在组件上滑动还是点击。这甚至在触摸持续期间都可能发生变化。也可能存在多个同时进行的触摸。
需要触摸响应器系统来允许组件协商这些触摸交互,而无需了解其父组件或子组件的任何额外信息。
最佳实践
为了让你的应用感觉很棒,每个操作都应该具有以下属性:
- 反馈/高亮 - 向用户展示是什么在处理他们的触摸,以及当他们释放手势时会发生什么
- 可取消性 - 在执行操作时,用户应该能够通过将手指拖开来中途取消触摸
这些功能让用户在使用应用时更舒适,因为它允许人们尝试和交互,而不必担心犯错。
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 是一个具有以下形式的合成触摸事件:
nativeEventchangedTouches- 自上次事件以来所有已更改触摸事件的数组identifier- 触摸的 IDlocationX- 触摸的 X 位置,相对于元素locationY- 触摸的 Y 位置,相对于元素pageX- 触摸的 X 位置,相对于根元素pageY- 触摸的 Y 位置,相对于根元素target- 接收触摸事件的元素的节点 idtimestamp- 触摸的时间标识符,用于速度计算touches- 屏幕上所有当前触摸的数组
捕获 ShouldSet 处理程序
onStartShouldSetResponder 和 onMoveShouldSetResponder 以冒泡模式调用,其中最深的节点最先被调用。这意味着当多个视图为 *ShouldSetResponder 处理程序返回 true 时,最深的组件将成为响应器。这在大多数情况下是可取的,因为它确保所有控件和按钮都是可用的。
但是,有时父组件希望确保它成为响应器。这可以通过使用捕获阶段来处理。在响应器系统从最深的组件冒泡之前,它将执行捕获阶段,触发 on*ShouldSetResponderCapture。因此,如果父视图想要阻止子视图在触摸开始时成为响应器,它应该有一个返回 true 的 onStartShouldSetResponderCapture 处理程序。
View.props.onStartShouldSetResponderCapture: evt => true,View.props.onMoveShouldSetResponderCapture: evt => true,
PanResponder
对于更高级的手势解释,请参阅 PanResponder。