简介
对于简单的 touch 事件,React Native有4个专门的 touch 组件进行处理
TouchableHighlight
TouchableNativeFeedback
TouchableOpacity
TouchableWithoutFeedback
他们可以绑定4种不同的响应方法
onPress
onPressIn
onPressOut
onLonePress
而对于手指滑动操作,利用上面的方法无法实现,这时就用到 gesture responder system
在React Native中,响应手势的基本单位是responder,具体来说,就是最常见的View组件。任何的View组件,都是潜在的responder,如果某个View组件没有响应手势操作,那是因为它还没有被“开发”。
将一个普通的View组件开发成为一个能响应手势操作的responder,非常简单,只需要按照React Native的gesture responder system的规范,在props上设置几个方法即可。具体如下:
View.props.onStartShouldSetResponder
View.props.onMoveShouldSetResponder
View.props.onResponderGrant
View.props.onResponderReject
View.props.onResponderMove
View.props.onResponderRelease
View.props.onResponderTerminationRequest
View.props.onResponderTerminate
这个是系统级别的手势相应,处理起来相对复杂,最常见的是使用 PanResponder
PanResponder
PanResponder 是 React Native 实现的一套手势相应方法,与gesture responder system 比起来,PanResponder 方法的抽象成都更高,使用起来也更加方便
使用方法很简单,只要View.props中设置相应的方法即可
panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
console.log(‘evt’, evt)
console.log(‘gestureState’, gestureState)
},
onPanResponderMove: (evt, gestureState) => {
console.log(‘evt’, evt)
console.log(‘gestureState’, gestureState)
},
onPanResponderRelease: (evt, gestureState) => {
console.log(‘evt’, evt)
console.log(‘gestureState’, gestureState)
},
})
render() {
>
…
}
简单说明5个方法
方法名 说明
onStartShouldSetPanResponder 负责处理用户通过触摸来激活一个 responser,如果返回值为 true,则表示这个 View 能够响应触摸手势被激活
onMoveShouldSetPanResponder 负责处理用户通过移动来激活一个 responser,如果返回值为 true,则表示这个 View 能够响应滑动手势被激活
onPanResponderGrant 如果组件被 responser 激活,则调用该方法
onPanResponderMove 用户滑动手指时,调用该方法
onPanResponderRelease 用户手指离开屏幕时,调用该方法
在5个方法中,我们能够使用的参数有2个,分别是 evt 和 gesture
参数 说明
evt 获取触摸的位置在被响应的 View 中的相对坐标,evt.nativeEvent.locationX 和 evt.nativeEvent.locationY(这个方法很实用)
gesture dx/dy:手势进行到现在的横向/纵向相对位移
vx/vy:此刻的横向/纵向速度
numberActiveTouches:responder上的触摸的个数
一个简单的拖拽 View 的例子
panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: ()=> true,
onPanResponderGrant: ()=>{
this._top = this.state.top
this._left = this.state.left
this.setState({bg: ‘red’})
},
onPanResponderMove: (evt,gs)=>{
console.log(gs.dx+’ '+gs.dy)
this.setState({
top: this._top+gs.dy,
left: this._left+gs.dx
})
},
onPanResponderRelease: (evt,gs)=>{
this.setState({
bg: ‘white’,
top: this._top+gs.dy,
left: this._left+gs.dx
})}
})
render() {
return(
style={[styles.rect,{
“backgroundColor”: this.state.bg,
“top”: this.state.top,
“left”: this.state.left
}]}
>
…
)
}
panResponser.gif
作者:石小泉
链接:https://www.jianshu.com/p/1b56b81e78d7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
ReactNative触摸事件处理
对RN触摸事件的捕获与冒泡机制的理解
组件A、B、C结构
组件A
组件B
组件C
捕获、冒泡机制
sequenceDiagram
A->>A: 是否捕获?若是则停止向下一级传递
A->>B:
B->>B: 是否捕获?若是则停止向下一级传递
B->>C:
C->>C: 是否捕获?若是则停止向下一级传递
C->>C: 是否声明成为响应者?无论是否,都冒泡。
C->>B:
B->>B: 是否声明成为响应者?无论是否,都冒泡。
B->>A:
A->>A: 是否声明成为响应者?
捕获期可通过onStartShouldSetResponderCapture 或 onMoveShouldSetResponderCapture回调决定是否阻止事件往下级组件传递。
冒泡期可通过onStartShouldSetResponder或onMoveShouldSetPanResponder回调决定是否成为响应者。若上级组件与下级组件都返回true,则下级组件成为当前触摸事件的响应者。(层级越深的组件优先级越高)
补充:
react-native Touch事件的拦截与分发
来自CSDN博文
RN如何处理触摸事件
View组件的pointerEvents属性
用于控制当前视图是否可以作为触控事件的目标。
auto:视图可以作为触控事件的目标。
none:视图不能作为触控事件的目标。
box-none:视图自身不能作为触控事件的目标,但其子视图可以。
View组件可用的手势
onTouchStart={()=>console.log(‘start’)}
onTouchMove={()=>console.log(‘move’)}
onTouchEnd={()=>console.log(‘end’)}
PanResponder 手势监视器
// 创建监视器
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: ()=>{},
…
})
// 在View中使用
/>
事件参数
每个事件都有两个返回参数nativeEvent, gestureState
nativeEvent包含以下属性
changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
identifier - 触摸点的 ID
locationX - 触摸点相对于父元素的横坐标(实践证明不好用,值会突变!原因未知)
locationY - 触摸点相对于父元素的纵坐标(实践证明不好用,值会突变!)
pageX - 触摸点相对于根元素的横坐标
pageY - 触摸点相对于根元素的纵坐标
target - 触摸点所在的元素 ID
timestamp - 触摸事件的时间戳,可用于移动速度的计算
touches - 当前屏幕上的所有触摸点的集合
gestureState包含以下属性:
stateID 此次触摸事件的ID
moveX 最近一次移动的屏幕坐标
moveY
x0 响应器产生时的屏幕坐标(手势第一个坐标)
y0
dx 触摸开始累积的横向路程
dy
vx 当前的横向移动速度
vy
numberActiveTouches 触摸点数量
事件生命周期
单点事件
onStartShouldSetResponderCapture 如果父视图想要阻止子视图响应 touch start 事件,它就应该设置这个方法并返回 true。
onStartShouldSetResponder 在用户开始触摸的时候(手指刚刚接触屏幕的瞬间),返回是否愿意成为响应者
onPanResponderGrant 这个视图开始响应触摸事件。此时需要高亮告诉用户正在响应。
onPanResponderStart 触摸事件正式被监视
onPanResponderEnd 触摸事件结束
onPanResponderRelease 在整个触摸事件结束时调用这个函数。
移动事件
onMoveShouldSetResponderCapture 如果父视图想要阻止子视图响应 touch move 事件时,它就应该设置这个方法并返回 true
onMoveShouldSetPanResponder 这个视图想要“认领”这个 touch move 事件吗?每当有 touch move 事件在这个视图中发生,并且这个视图没有被设置为这个 touch move 的响应时,这个函数就会被调用。
onPanResponderGrant 监视器发出通知开始工作
onPanResponderMove 当用户正在屏幕上移动手指时调用这个函数。
异常事件
onPanResponderReject 有一个响应器正处于活跃状态,并且不会向另一个要求响应这个事件的视图释放这个事件。
onPanResponderTerminationRequest 其他某个视图想要成为事件的响应者,并要求这个视图放弃对事件的响应时,就会调用这个函数。如果允许释放响应,就返回true。
onPanResponderTermination 响应被从这个视图上“劫走”了。可能是在调用了 onResponderTerminationRequest 之后,被另一个视图“劫走”了(见 onresponderterminationrequest), 也可能是由于 OS 无条件终止了响应(比如说被 iOS 上的控制中心/消息中心)
行为影响状态,状态影响视图