在原生开发中,触摸事件的流程了解还是比较重要的,那么,在ReactNative中,这一点也是一样的,那么接下来就来总结一下ReactNative中手势识别方面的一些信息吧
一、常用触摸组件
在ReactNative中,有几个组件,基本上可以满足大部分的需求,这些组件分别是
TouchableHighlight------按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮
TouchableNativeFeedback------使其可以正确响应触摸操作(仅限Android平台)
TouchableOpacity------按下的时候,封装的视图的不透明度会降低,即无特效
TouchableWithoutFeedback------除非你有一个很好的理由,否则不要用这个组件
上面这些组件的使用方式都差不多,举例:
TouchableHighlight
onPressIn={() => console.log("onPressIn")}//开始按下
onPressOut={() => console.log("onPressOut")}//开始离开
onPress={() => console.log("onPress")}//点击事件
onLongPress={() => console.log("onLongPress")}//长按事件
>
<Image
style={styles.button}
source={require('./img/rn_logo.png')} />
</TouchableHighlight>
二、组件触摸事件处理
1、方法说明
关键组件:PanResponder
提供的可以处理的方法:
- onMoveShouldSetPanResponder: (e, gestureState) => bool
- 触摸过程中,询问是否要成为响应者,返回true则表示要成为响应者,然后进入事件协调
- onMoveShouldSetPanResponderCapture: (e, gestureState) => bool
- 触摸过程中,容器组件被触发,是否要劫持事件,返回true表示劫持,则子组件无法再接收到事件
- onStartShouldSetPanResponder: (e, gestureState) => bool
- 触摸开始时,询问是否要成为响应者,返回true则表示要成为响应者,然后进入事件协调
- onStartShouldSetPanResponderCapture: (e, gestureState) => bool
- 触摸开始,容器组件被触发,是否要劫持事件,返回true表示劫持,则子组件无法再接收到事件
- onPanResponderReject: (e, gestureState) => {...}
- 表示申请失败了,这意味者其他组件正在进行事件处理,并且它不想放弃事件处理
- onPanResponderGrant: (e, gestureState) => {...}
- 表示申请成功,组件成为了事件处理响应者,这时组件就开始接收后序的触摸事件输入
- onPanResponderStart: (e, gestureState) => {...}
- 表示手指按下时,成功申请为事件响应者的回调
- onPanResponderEnd: (e, gestureState) => {...}
- 表示组件结束事件响应的回调
- onPanResponderRelease: (e, gestureState) => {...}
- 表示触摸完成(touchUp)的时候的回调,表示用户完成了本次的触摸交互,这里应该完成手势识别的处理,这以后,组件不再是事件响应者,组件取消激活
- onPanResponderMove: (e, gestureState) => {...}
- 表示触摸手指移动的事件,这个回调可能非常频繁,所以这个回调函数的内容需要尽量简单
- onPanResponderTerminate: (e, gestureState) => {...}
- 事件被其他情况或组件抢走了事件处理权,这个回调也会发生在系统直接终止组件的事件处理,例如用户在触摸操作过程中,突然来电话的情况
- onPanResponderTerminationRequest: (e, gestureState) => {...}
- 有其他组件申请处理事件,如果回调函数返回为
true
,则表示同意释放响应者角色,同时会回调如下函数,通知组件事件响应处理被终止了
- 有其他组件申请处理事件,如果回调函数返回为
- onShouldBlockNativeResponder: (e, gestureState) => bool
- 表示是否用 Native 平台的事件处理,默认是禁用的,全部使用 JS 中的事件处理,注意此函数目前只能在 Android 平台上使用。
事件参数说明:
1、gestureState
stateID
- 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。moveX
- 最近一次移动时的屏幕横坐标moveY
- 最近一次移动时的屏幕纵坐标x0
- 当响应器产生时的屏幕坐标y0
- 当响应器产生时的屏幕坐标dx
- 从触摸操作开始时的累计横向路程dy
- 从触摸操作开始时的累计纵向路程vx
- 当前的横向移动速度vy
- 当前的纵向移动速度numberActiveTouches
- 当前在屏幕上的有效触摸点的数量
2、e
- nativeEvent
changedTouches
- 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)identifier
- 触摸点的IDlocationX
- 触摸点相对于父元素的横坐标locationY
- 触摸点相对于父元素的纵坐标pageX
- 触摸点相对于根元素的横坐标pageY
- 触摸点相对于根元素的纵坐标target
- 触摸点所在的元素IDtimestamp
- 触摸事件的时间戳,可用于移动速度的计算touches
- 当前屏幕上的所有触摸点的集合
这些数据中,最常用的是 locationX
和 locationY
数据,需要注意的是,因为这里是 Native 的数据,所以他们的单位是实际像素。如果要转换为 RN 中的逻辑单位,可以示使用如下方法:
var pX = evt.nativeEvent.locationX / PixelRatio.get();
2、基本处理逻辑
假设我门有这样一个布局
如果上面三个组件全都申请了处理触摸事件,即全都写了方法:
onStartShouldSetPanResponder
onMoveShouldSetPanResponder
并且全都返回true,那么在这种情况下则是C组件得到响应成为响应者
然后是各个事件之间的关系,这个之后补上示意图