React Native手势处理详解
转自:http://www.race604.com/react-native-touch-event/,深度好文!!! 触控是移动设备的核心功能,也移动应用交互的基础,Android 和 iOS 各自都有完善的触摸事件处理机制。React Native(以下简称 RN)提供了一套统一的处理方式,能够方便的处理界面中组件的触摸事件、用户手势等。本文尝试介绍 RN 中触摸事件处理。 1. RN 基本触摸组件RN 的组件除了 Text,其他组件默认是不支持点击事件,也不能响应基本触摸事件,所以 RN 中提供了几个直接处理响应事件的组件,基本上能够满大部分的点击处理需求
它们的基本使用方法如下,这里以 TouchableHighlight 为例: <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> RN 中提供的触摸组件使用非常简单,可以参考官方文档,这里也不做详细的介绍了。下面主要介绍用户触摸事件处理。 2. 单组件触摸事件处理我们知道,RN 的组件默认不进行处理触摸事件。组件要处理触摸事件,首先要“申请”成为摸事件的响应者(Responder),完成事件处理以后,会释放响应者的角色。一个触摸事件处理周期,是从用户手指按下屏幕,到用户抬起手指抬起结束,这是用户的一次完整触摸操作。 单个组件的单次操作交互处理的生命周期如下: 我们来详细分析一下事件处理的生命周期,在整个事件处理的过程中,组件有可能处于两种身份中的一种,并且可以相互切换:非事件响应者和事件响应者。 非事件响应者默认情况下,触摸事件输入不会直接传递给组件,不能进行事件响应处理,也就是非事件响应者。如果组件要进行触摸事件处理,首先要申请成为事件响应者,组件有如下两个属性可以做这样的申请:
假如组件通过上面的方法返回了
事件响应者如果通过上面的步骤,组件申请成为了事件响应者,后续的事件输入都会通过回调函数通知到组件,如下:
从前面的图中也看到,在组件成为事件响应者期间,其他组件也可能会申请触摸事件处理。此时 RN 会通过回调询问你是否可以释放响应者角色让给其他组件。回调如下: View.props.onResponderTerminationRequest: (evt) => bool 如果回调函数返回为 View.props.onResponderTerminate: (evt) => {} 这个回调也会发生在系统直接终止组件的事件处理,例如用户在触摸操作过程中,突然来电话的情况。 事件数据结构从前面我们看到,触摸事件处理的回调都有一个
这些数据中,最常用的是 var pX = evt.nativeEvent.locationX / PixelRatio.get(); 3. 嵌套组件事件处理上一小节介绍的都是针对单个组件来说,事件处理的流程和机制。但是前面也提到了,当组件需要作为事件处理响应者时,需要通过 在 RN 中,默认情况下使用冒泡机制,响应最深的组件最先开始响应,所以前面描述的这种情况,如图中,如果 A、B、C 三个组件的
可以把这种劫持机制看成是一种下沉机制,与上面的冒泡机制对应,我们可以总结 RN 事件处理流程如下图: 注,图中的*表示可以为Start或者Move,例如on*ShouldSetResponderCapture表示 触摸事件开始,首先调用 A 组件的 注意到,图中还有 4. 手势识别前面只是介绍了简单的触摸事件处理机制及其使用方法,其实连续的触摸事件,可以组成一些更高级手势,例如我们最常见的滑动屏幕内容,双指缩放(Pinch)或者旋转图片都是通过手势识别完成的。 因为有些手势是很常用的,RN 也提供了内置的手势识别库
可以看到,这些接口与前面接收的基础回调基本上是一一对应的,其功能也是类似,这里就不再赘述。这里有一个特别的回调
下面介绍一个简单的实例,本例实现可以使用手指拖动界面的圆形控件,使用实例如下: import React from 'react'; import { AppRegistry,PanResponder,StyleSheet,View,processColor,} from 'react-native'; var CIRCLE_SIZE = 80; var CIRCLE_COLOR = 'blue'; var CIRCLE_HIGHLIGHT_COLOR = 'green'; var PanResponderExample = React.createClass({ statics: { title: 'PanResponder Sample',description: 'Shows the use of PanResponder to provide basic gesture handling.',},_panResponder: {},_previousLeft: 0,_previousTop: 0,_circleStyles: {},circle: (null : ?{ setNativeProps(props: Object): void }),componentWillMount: function() { this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt,gestureState) => true,onMoveShouldSetPanResponder: (evt,onPanResponderGrant: this._handlePanResponderGrant,onPanResponderMove: this._handlePanResponderMove,onPanResponderRelease: this._handlePanResponderEnd,onPanResponderTerminate: this._handlePanResponderEnd,}); this._previousLeft = 20; this._previousTop = 84; this._circleStyles = { style: { left: this._previousLeft,top: this._previousTop } }; },componentDidMount: function() { this._updatePosition(); },render: function() { return ( <View style={styles.container}> <View ref={(circle) => { this.circle = circle; }} style={styles.circle} {...this._panResponder.panHandlers} /> </View> ); },_highlight: function() { const circle = this.circle; circle && circle.setNativeProps({ style: { backgroundColor: processColor(CIRCLE_HIGHLIGHT_COLOR) } }); },_unHighlight: function() { const circle = this.circle; circle && circle.setNativeProps({ style: { backgroundColor: processColor(CIRCLE_COLOR) } }); },_updatePosition: function() { this.circle && this.circle.setNativeProps(this._circleStyles); },_handlePanResponderGrant: function(e: Object,gestureState: Object) { this._highlight(); },_handlePanResponderMove: function(e: Object,gestureState: Object) { this._circleStyles.style.left = this._previousLeft + gestureState.dx; this._circleStyles.style.top = this._previousTop + gestureState.dy; this._updatePosition(); },_handlePanResponderEnd: function(e: Object,gestureState: Object) { this._unHighlight(); this._previousLeft += gestureState.dx; this._previousTop += gestureState.dy; },}); var styles = StyleSheet.create({ circle: { width: CIRCLE_SIZE,height: CIRCLE_SIZE,borderRadius: CIRCLE_SIZE / 2,backgroundColor: CIRCLE_COLOR,position: 'absolute',left: 0,top: 0,container: { flex: 1,paddingTop: 64,}); 可见,在 <View {...this._panResponder.panHandlers} /> 其余的代码也比较简单,这里就不详述了。 5. 总结通过上面的介绍,可以看到 RN 中提供了类似 Native 平台的事件处理机制,所以也可以实现各种的触摸事件处理,甚至也可以实现复杂的手势识别。 在嵌套组件的事件处理中,RN 中提供了“冒泡”和“下沉”两个方向的事件处理,这有点类似于 Android Native 上不久前才支持的NestedScrolling,这就提供更加强大的事件处理机制。 另外需要注意,因为 RN 的异步通信和执行机制,前面描述的所有回调函数都是在 JS 线程中,并不是 Native 的 UI 线程,而 Native 平台的 Touch 事件都是在 UI 线程中。所以在 JS 中通过 Touch 或者手势实现动画,可能会延迟的问题。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |