React Native 手势触摸事件详解(基础篇)

博客产出拖延了很久,老早定的主题现在才开始写。之前群里朋友对于React Native(以下简称RN)中手势触摸相关问题提出的频率很高,并且在实际开发过程中较难理解和处理。本篇内容将围绕触摸事件相关问题一探究竟,也作为记录供后期参考。

作为与用户交互的第一层,触摸事件直接影响着用户行为体验。在Android 和 iOS 平台设备中,对于触摸机制做了非常完善的封装,能够很方便的帮助开发者处理基本的触摸行为操作,原生平台通过注册Listener的方式可以轻松的实现单击,双击等操作。在RN中同样提供了与Native触摸事件映射一致的处理方式,方便React Native开发者处理触摸行为,定义触摸操作。

一、RN系统触摸组件 RN中实现按钮单击组件的方式很简单,系统为我们提供了四种方式:

(1)TouchableOpacity

(2)TouchableHightLight

(3)TouchableNativeFeedback(仅限Android)

(4)TouchableWithoutFeedback

以上四种方式相信大家都不陌生,都是以 Touchable* 开始,实现单击事件只需要声明onPress属性即可。以 TouchableOpacity 为例:

<TouchableOpacity onPress={()=>this.onPress()}> 单击按钮 除了onPress属性,系统还为我们提供了:

(1)onLongPress

(2)onPressIn

(3)onPressOut

顾名思义,onLongPress即为长按点击、onPressIn点击开始(手指按下)、onPressOut点击结束(手指离开)。

<TouchableOpacity onPressIn={()=>this.onPressIn()} onPressOut={()=>this.onPressOut()} onLongPress={()=>this.onLongPress()} onPress={()=>this.onPress()}> 点击按钮 四个方法也提供了触摸事件数据,所以也可以写成如下:

<TouchableOpacity onPressIn={(evt)=>this.onPressIn(evt)} onPressOut={(evt)=>this.onPressOut(evt)} onLongPress={(evt)=>this.onLongPress(evt)} onPress={(evt)=>this.onPress(evt)}> 点击按钮 evt 中包含了关于触摸事件相关的数据,大部分情况下我们只需要关心 nativeEvent,其大致结构如下:

nativeEvent 可以看到 nativeEvent 中包含了手指触摸的一些坐标及时间戳参数,关于具体的参数含义我们后面再进行具体探讨。

二、自定义触摸事件处理 从Touchable* 组件的使用可以看出,其内部实现了触摸之后的事件处理。开发者以简单的属性声明方式,即可完成对于点击事件的操作。且RN组件默认不支持触摸事件的处理(Text组件除外),如果想要处理触摸事件,首先要【申请】成为事件响应者,当成为事件响应者之后,即可处理发生在该组件之上的触摸事件,例如:按下(Start)、移动(Move)、弹起(Release),最终释放响应行为。即一次完整的触摸流程大致如下:

申请成为触摸事件响应者 -> 成为触摸事件响应者 -> 处理触摸事件 -> 释放触摸事件 -> 触摸事件结束

整个事件流程中,组件的事件身份分为两种:非事件响应者、事件响应者。

非事件响应者 授权

在上述部分我们提到,RN中的组件除Text外,默认是不能进行触摸事件处理响应的,即非事件响应者。如果要进行触摸事件处理,首先需要申请成为触摸事件响应者。对应申请的处理方法如下:

View.props.onStartShouldSetResponder(evt): => bool

View.props.onMoveShouldSetResponder(evt): => bool

从名称可以看出,onStartShouldSetResponder在手势触开始时(按下)被触发,onMoveShouldSetResponder在手势滑动时被触发。两个方法都需要返回boolean类型值:onStartShouldSetResponder 在手指按下屏幕时,RN系统会询问当前组件是否需要申请成为事件响应者,true表示成为事件响应者,false则不处理。同样,onMoveShouldSetResponder表示手指在屏幕滑动时,RN系统询问当前组件是否需要申请成为事件响应者,true表示成为事件响应者,false则不处理。

授权结果

当接收到上述方法返回true时,组件申请成为响应者,并处理接收后续触摸事件。在同一时间只能有一个事件处理者,此时,RN系统会协调当前所有组件到事件处理,所以不是每个组件申请都能成功,RN 通过如下两个回调来通知告诉组件它的申请结果:

View.props.onResponderGrant: (evt) => { }

View.props.onResponderReject: (evt) => { }

onResponderGrant表示申请成功,组件已经成为事件响应者,并且接收后续的触摸事件。在该方法中,我们可以做一些手势事件初始化的操作。onResponderReject表示申请失败,失败的原因一般为其他组件正在进行触摸事件的处理,并且不放弃当前触摸事件的权限,在你申请时被拒。

事件响应者 通过上述步骤,当组件申请成为事件响应者后,后续当触摸事件行为都会被该组件拦截处理,并触发对应都行为函数。触摸事件行为函数如下:

View.props.onResponderStart: (evt) => { }

View.props.onResponderMove: (evt) => { }

View.props.onResponderEnd: (evt) => { }

View.props.onResponderRelease: (evt) => { }

可以很明显的看到,四个事件行为方法都是以【onResponder】作为前缀,后面紧接行为方式名称。

Start:表示手指按下,开始进行触摸行为。

Move:手指触摸屏幕并进行移动,此回调函数触发非常频繁,尽可能不要做过多任务处理。

End:触摸行为结束,手指弹起离开屏幕。

Release:当手指弹起离开屏幕时,事件行为结束,一次完整的触摸事件结束,并释放当前触摸行为权限,当前组件不再是事件响应者。

释放响应者权限 当前组件正在进行触摸事件行为处理且没有结束时,其他组件也可能会请求系统申请成为事件响应者,此时RN会询问当前组件

是否可以释放当前响应者身份,将权限让给其他组件。对应的函数如下:

View.props.onResponderTerminationRequest: (evt) => bool

View.props.onResponderTerminate: (evt) => {}

Termination 即中止,onResponderTerminationRequest 方法需要返回boolean类型值,true 表示可以立刻释放当前响应者角色,并触发 onResponderTerminate 方法,告知当前组件触摸事件被中止。

触摸事件被意外中断也会触发 onResponderTerminate 方法,例如:手机来电,自动关机,消息等。

触摸事件响应参数 从系统提供的 Touchable* 点击组件,到自定义触摸组件,可以发现触摸行为函数都有 event 参数,在文章开始介绍 Touchable* 组件时,我们说到 event 参数中包含一个触摸事件数据 nativeEvent,在该属性中包含了触摸事件中的相关参数,具体如下:

changedTouches:[{…}]  触摸事件集合,记录从上次到本次触摸事件到所有事件,在触摸过程中,由于非常频繁,可能有没有及时反馈,系统用这个属性来批量上报。

identifier:   手指触摸事件ID,多点触控场景下,用来区分手指的触摸事件。

locationX:  触摸点在组件横向上的位置。

locationY:  触摸点在组件纵向上的位置。

pageX:       当前组件触摸点相对屏幕横向上的位置。

pageY:       当前组件触摸点相对屏幕纵向上的位置。

target:        当前组件ID。

timestamp:当前触摸的事件的时间戳,可以用来进行滑动计算。

touches:[{…}]   触摸事件集合,多点触摸场景下,包含当前所有触摸点的事件参数。

在日常开发中常用的属性是 locationX|Y、pageX|Y。常用触摸事件的参数是从原生层传递到RN层,即参数数值是作为原生层到像素值,如果需要转换成RN中到逻辑单位,可以用如下方式:

const px = evt.nativeEvent.locationX / PixelRatio.get()

三、触摸事件拦截 在第二小节中,我们花了大量篇幅来介绍自定义触摸事件的相关行为函数,触摸参数等。可以看到所有的触摸行为都是基于一个View组件之上。在日常开发中,我们肯定离不开View组件嵌套实现一个视图效果。那么嵌套组件对于触摸事件响应情况又是怎样的?

了解 Native 开发的朋友肯定不陌生,在Native层系统处理触摸事件传递方式使用的是 冒泡机制。即事件的响应是从布局最底层的组件开始,逐层向父布局组件传递。同样,RN系统也提供了与 Native 层相同的触摸事件传递机制,用来保证在嵌套布局中的所有组件都可以得到响应处理。在某些情况下,父组件可能需要单独处理触摸事件,不需要交给子组件处理,即父组件拦截掉触摸事件的响应,消耗完成不再向下传递。同样,RN系统提供了两个函数,用来实现授权父组件事件拦截机制:

View.props.onStartShouldSetResponderCapture: (evt)=> bool

View.props.onMoveShouldSetResponderCapture: (evt)=> bool

在触摸事件 开始,RN父布局组件会回调 onStartShouldSetResponderCapture,询问是否要拦截事件,自己接收处理, true 表示拦截。在触摸 滑动 事件时,RN父布局组件会回调 onMoveShouldSetResponderCapture,询问是否要拦截事件,自己接收处理, true 表示拦截。

举个?,假设有A,B,C 三个组件,布局为 C是B的子组件,B是A的子组件:

嵌套组件 (1)A组件的 onStartShouldSetResponderCapture 返回 false,表示不拦截,事件传递到B。

(2)B组件的onStartShouldSetResponderCapture 返回 true,表示拦截,事件不再向下传递,即事件不会传递到C。

(3)此时B组件开始向系统申请事件响应者权限,即调用 on*ShouldSetResponder,如果返回 true,则表示授权成功,此时B组件成为事件响应者,执行Start、Move 等事件行为。

(4)如果B组件不申请,则系统询问A组件是否申请成为事件响应者。即调用A组件等on*ShouldSetResponder,如果返回 true,则表示授权成功,此时A组件成为事件响应者,执行 Start、Move 等事件行为。

emm... 终于到了喘口气到时候了,关于触摸事件到处理流程,相信大家看到这里都有了自己的认知与理解。下一篇,我们会从更高级的API来了解RN中的触摸事件,并通过代码运行的结果更深的去看事件触发的行为方式。溜了....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Native是一种用于构建跨平台移动应用的框架,它可以使用JavaScript和React技术来开发原生移动应用。下面是React Native的一些常见用法: 1. 创建一个新的React Native项目: 使用React Native CLI命令行工具创建一个新的React Native项目,例如: ``` npx react-native init MyApp ``` 2. 编写组件: 在项目中编写React Native组件,可以使用class组件或函数式组件来定义自定义组件。组件可以包含UI元素、样式和交互逻辑等。 3. 导入和使用内置组件: React Native提供了许多内置的UI组件和API,可以通过导入和使用它们来构建应用。例如,可以使用View、Text、Image、Button等组件来创建用户界面。 4. 样式和布局: 使用StyleSheet.create()方法创建样式对象,并将其应用于组件的style属性中。可以使用Flexbox布局模型来进行灵活的布局。 5. 处理用户输入和交互: 可以使用内置的触摸事件(如onPress、onLongPress等)来处理用户的输入和交互。还可以使用手势识别器(如PanResponder)来实现更复杂的手势操作。 6. 导航和路由: React Navigation是一个常用的第三方库,用于实现导航和路由功能。可以使用它来创建导航栏、选项卡、侧边菜单等导航组件。 7. 调用原生模块和API: 如果需要访问设备的原生功能和API,可以使用React Native提供的桥接机制来调用原生模块和API。这允许开发者编写原生代码并与React Native应用进行交互。 8. 调试和测试: React Native提供了一些调试和测试工具,如React Native Debugger、Reactotron等,可以帮助开发者调试和测试应用。 以上是React Native的一些常见用法,但还有很多其他的功能和工具可供使用。建议查阅React Native官方文档和相关教程来深入了解和学习React Native的使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值