十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
目前手机市场上,全面屏时代已经势不可挡,为了增大屏幕,一个个物理按键已渐渐消失在手机上。那么,手势将成为在移动应用开发中一个重要的组成部分,移动设备上手势识别要比 web 端复杂得多,往往用户的一个手势,我们在 APP 上要通过好几个阶段去判断用户的真实意图是什么,在 ReactNative (以下简称 RN)中针对手势处理也提供了从最基本的点击手势到复杂的滑动等一系列解决方案,让我们一起去看看。
成都创新互联自成立以来,一直致力于为企业提供从网站策划、网站设计、网站建设、网站制作、电子商务、网站推广、网站优化到为企业提供个性化软件开发等基于互联网的全面整合营销服务。公司拥有丰富的网站建设和互联网应用系统开发管理经验、成熟的应用系统解决方案、优秀的网站开发工程师团队及专业的网站设计师团队。
RN基本触控组件
RN 的组件除了 Text,其他组件默认是不支持点击事件的,也不能成为一个触摸事件的响应者。RN 提供了几个比较直接的处理响应事件的组件,基本上能满足大部分的点击事件的处理需求。
TouchableHighlight
TouchableNativeFeedback (仅限 Android 平台)
TouchableOpacity
TouchableWithoutFeedback
这几个组件的功能和使用方法基本类似,只是就 Touch 的效果反馈上有所差异,他们有如下几个回调方法:
onPressIn:用户触摸开始的时候,也就是手指刚落在 Touch 点击区域内的时触发
onPressOut:用户触摸结束的时候,也就是手指从 Touch 点击区域内抬起的时触发
onPress:用户完成一次从 onPressIn 到 onPressOut 的过程,且时间很短,即一次快速点击操作时触发
onLongPress:用户触发 onPressIn 且手指一段时间内没有抬起时触发
这里以 TouchableHighlight 为例,贴一个 Touch 的基本用法:
RN 中提供的 Touch 组件的使用非常简单,可以参考官方文档,这里就不做详细的介绍了,我们主要来说下用户的触摸事件处理。
gesture responder system
在 RN 中,响应手势的基本单位是 responder,具体点说就是最常见的 View 组件。任何的 View 组件都可以成为一个手势的响应者。其实要把一个普通的 View 组件开发成为一个能响应手势操作的 responder 很简单,话不多说,我们举栗子!
乍一看,WillMount 里面的这几个方法名字又长又奇怪,但是等你了解了 RN 手势响应的流程了之后,记忆这几个方法就非常简单了。在我们探索这几个方法之前,我们首先要记住一个重要的点:
一个 RN 应用中只能存在一个 responder!
一次正常的手势操作的流程如下所示:
是否响应 Touch 或者 move 手势->grant(被激活) ->move->release (结束事件)
与流程相对应的方法是:
onStartShouldSetResponder(event) => true:在用户开始进行触摸操作时(手指刚刚接触屏幕的瞬间),询问是否申请成为触摸事件的响应者,返回 true 为需要成为响应者。
onMoveShouldSetResponder(event) => true:如果绑定的View不是响应者,那么会在用户的触摸点开始移动的时候再次询问是否申请成为触摸时间的响应者,返回true
为需要成为响应者。
假设组件通过上面的方法返回了 true,表示发出了申请需要成为响应者,但是我们前面说过,一个 RN 应用中只能有一个 responder,那么接下来就需要协调所有组件的请求,看看这个响应者的位置给谁。
onResponderGrant:(event) => {}:View 申请成功,并成为了响应者。一般情况下,这时开始,组件进入了激活状态,并进行一些事件处理或者手势识别的初始化。
onResponderReject: (event) =>{}:View 申请失败了,这就意味着有其他的组件正在成为或者已经成为了响应者,并且他不愿意交出这个权利。所以你被拒绝了~
如果你成为了响应者,那么会收到后续的事件输入并由你来决定他的行为动作:
onResponderMove: (event) => 表示触摸手指的移动事件,这个回调在一次完成的手势动作中可能会非常频繁的调用,所以这个回调函数里面的内容需要尽量简单
onResponderRelease: (event) => 表示触摸完成,相当于前面讲的 Touch 里面的 onPressOut 方法,表示用户已经完成了本次的触摸操作,同时会释放响应者这个权利。
在你成为响应者期间,其他组件也有可能会申请成为响应者,那么此时RN会通过回调来询问当前的响应者是否放权给其他申请者。回调如下:
onResponderTerminationRequest: (event) => true:如果我们返回的是 true,那就代表当前响应者同意放权,让其他的组件来当响应者,自己回归平淡的生活,同时也会回调一个函数,通知组件事件响应处理被终止了:
onResponderTerminate: (event) => {}:这个回调也会发生在系统直接终止组件的触摸事件处理中,比如用户在进行触摸操作的时候,来电话了,或者意外闪退了。
相信大家都发现了,所有的方法都有一个 event 参数,里面包含了一个触摸事件数据 nativeEvent,nativeEvent 具体结构如下图:
chanedTouches:event 数组,从上次回调上报的触摸事件,到这次上报之间的所有事件数组。因为在用户触摸过程中会产生很多事件,有时候可能还没来得及上报,系统就用这种方式批量上报
identifier:触摸的 ID,这个 ID 存在周期为从触摸开始到释放为止,主要是用来区别在多点触控的情况下,区分是哪个手指的触摸事件。
locationX 和 locationY:触摸点相对于组件的位置
pageX 和 pageY:触摸点相对于屏幕的位置
target:接收当前触摸事件的组件 ID
timestamp:当前触摸的事件的时间戳,可以用来进行滑动的相关计算(速度,停留时长)
touches:event 数组,多点触摸的时候,包含当前所有触摸点的事件
冒泡机制和事件捕获
先前我们都是针对单一组件来说的,但是在实际开发过程中,我们往往会遇到很多嵌套之类的组件,那如果在我们多重嵌套的组件中,每层组件绑定了一个手势响应且 onStartShouldSetResponder 或者 onMoveShouldSetResponder 回调都返回了 true 来申请成为响应者的话,又会怎么样呢?我们举个栗子来看看:
在这个大栗子中,我们嵌套了两层组件,使得组件布局如图:
在RN中,默认情况下会遵循冒泡机制,也就是嵌套最深的组件最先开始响应,那么我们栗子中的三层组件的 onStartShouldSetResponder 或者 onMoveShouldSetResponder 全部都返回 true 的情况下,那么 C 组件会优先成为事件响应者。但在我们的实际开发中,可能你需要的是父组件去处理触控事件,而禁止子组件响应,那肿么办?。RN 给我们提供了一个事件捕获机制,也就是在触摸事件通过冒泡机制往下传递的时候,先询问上层有申请的组件是否捕获该事件,不给子组件传递事件,即上面的栗子中,正常情况下通过冒泡机制,我们的触控事件会 A->B->C 这样传递到 C 去响应事件,当 A 传递到 B 时,会询问 A 是否捕获这个触控事件并且不再向下传递给 B 和 C,如果 A确认捕获,那么 A 即成为这个事件的响应者。具体的回调是:
onStartShouldSetResponderCapture: () => true :在触摸事件开始的时候,RN 容器的组件就会收到这么一个回调函数,询问是否捕获事件成为响应者,如果返回true,表示确认捕获事件
onMoveShouldSetResponderCapture: () =>true :在触摸事件开始移动的时候,再次询问是否捕获事件成为响应者,如果返回 true,表示确认捕获事件
PanResponder
除了 gesture responder system 之外,RN 还抽象出了一套 PanResponder 方法,这套方法的好处在于,使用起来更方便,在不改变原有的逻辑和流程的前提下,提供了更多的参数,包含了手势进行过程中更多的信息,让我们更好的去理解和处理用户的手势意图,话不多说,直接上栗子。
在上面的栗子中,我们实现了在一个白色有边框的事件响应者开始响应事件而变成绿色,然后实现拖拽效果并且在拖拽过程中变成红色,最后在释放手指又变回白色的这么一个过程。
大体上和 gesture responder system 一样,我们要注意的就是几个方法的写法加上了 Pan,并且几个回调函数多了一个 gesture 参数,他具体长这样的:
dx 和 dy:从触摸操作开始到现在的累积横向/纵向路程
moveX 和 moveY:最近一次移动时的屏幕横/纵坐标
numberActiveTouches:当前在屏幕上的有效触摸点的数量
stated:和之前一样,用来识别手指的ID
vx 和 vy:当前横向/纵向移动的速度
x0 和 y0:当触摸操作开始时组件相对于屏幕的横/纵坐标
总结
以上是我对 RN 的一些基础学习和理解,只举了一些简单的栗子,要在项目里实现一些更为复杂的手势操作,还需要进一步的摸索研究。另外需要注意的是,上述的回调函数都是在 JS 线程中进行的,可能会有些许延迟。
【海说软件接受各种技术咨询及开发业务】
-END-