rn手势功能实战

目前手机市场上,全面屏时代已经势不可挡,为了增大屏幕,一个个物理按键已渐渐消失在手机上。那么,手势将成为在移动应用开发中一个重要的组成部分,移动设备上手势识别要比 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 线程中进行的,可能会有些许延迟。

原文发布于微信公众号 - 编程坑太多(idig88)

原文发表时间:2018-03-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大数据钻研

Meta标签的那些事

Meta标签是HTML语言head区的一个辅助性标签,它位于HTML文档头部的head标记和title标记之间,它提供用户不可见的信息。它可用于浏览器(如何显示...

28950
来自专栏鹅厂优文

小程序入坑指南 | 鹅厂优文

前段时间,手上刚好接手一个小程序的项目,心想之前自学过一段时间的小程序,终于有项目可以练练手了,可惜,万万没想到,加了两个周末的班结果却成了飞机稿...

1.6K110
来自专栏Hongten

一个小巧的HTML编辑器_CLEditor_源码下载

CLEditor是一个开源的jQuery插件提供了一个轻量级的、全功能、跨浏览器、可扩展、

39410
来自专栏DeveWork

WordPress 根据浏览器 user-agent 按需加载CSS 文件

在进行前端开发的时候,为了兼容性,比如hack 那个讨厌的IE 浏览器,我们常常需要<!--[if IE X]>这类IE 判断代码来实现hack 的效果。而在W...

21980
来自专栏java系列博客

IDEA全局护眼色

21130
来自专栏程序员的知识天地

python爬虫爬取海量高清图片!这绝对是动漫迷们的福音

这链接还是比较好获取的,直接 F12 审核元素,或者右键查看代码,手机上chrome和firefox在url前面加上 "view-source"

41710
来自专栏杨逸轩 ' sBlog

利用JS生成当前页面二维码,简单实用

46060
来自专栏前端小叙

如何把大段文字转为带html标签的文字

开发网页的时候,有时候会遇到大段的隐私声明,用户协议等等,我们呀要复制粘贴展示出来,必须加大量的p标签,h1,h2,空格符,br标签,这对我们来说无疑是泪崩的,...

74510
来自专栏北京马哥教育

最全整理 | 121个Ubuntu终端常用快捷键

Ubuntu中的许多操作在终端(Terminal)中十分的快捷,记住一些快捷键的操作更得心应手。在Ubuntu中打开终端的快捷键是Ctrl+Alt+T。其他的一...

368120
来自专栏IMWeb前端团队

基于vue2.0+vuex+localStorage开发的本地记事本

本文采用vue2.0+vuex+localStorage+sass+webpack,实现一个本地存储的记事本。兼容PC端和移动端。 在线预览地址:DEMO 功能...

31060

扫码关注云+社区

领取腾讯云代金券