React Native 五:手势

一、Touchable手势
1.React Native提供了4个组件来做这个事情,具体如下:
TouchableHighlight:高亮触摸,用户点击时,会产生高亮效果
TouchableNativeFeedback:
TouchableOpacity:透明触摸,用户点击时,点击的组件不会出现任何视觉变化;
TouchableWithoutFeedback:无反馈触摸,用户点击时,点击的组件不会有任何视觉变化;
2.这4个组件,我们可以应用某个部分绑定上Touch事件,并支持一下方法
onPress:
onPressIn:
onPressOut:
onLongPress:
3.下面我们以实例演示下,相关代码实现:
Index.android.js文件
import React,{
  … … 
  TouchableHighlight,} from 'react-native';

class AwesomeProject extends Component {
  show(text) {
    alert(text);
  }
  //手势相关事件的实现
  onPressIn(){
    this.start = Date.Now()
    console.log("press in")
  }
  onPressOut(){
    console.log("press out")
  }
  onPress(text){
    console.log("press")
    alert(text);
  }
  onLonePress(){  AppRegistry,console.log("long press "+(Date.Now()-this.start))
  }
  render() {
    return(
      <View style={styles.container}>
        //TouchableHighlight包裹绑定Touch手势的组件,并实现支持的4个事件
        <TouchableHighlight style={styles.touchable} onPressIn={this.onPressIn} onPressOut={this.onPressOut}
          onPress={this.onPress.bind(this,'点击了吗?')} onLongPress={this.onLonePress}>
          <View style={styles.button}></View>
        </TouchableHighlight>
      </View>
    )
  };
}
var styles = StyleSheet.create({
  container: {
    flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},button:{
    width: 200,height: 200,borderRadius: 100,backgroundColor: 'red'
  },touchable: {
    borderRadius: 100
  },});
AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
4.点击,我们看见具体的运行效果如下:

5.我们使用Debug模式,研究下4个手势出现的条件和顺序;
摇晃手机,选择Debug JS;

打开Chrome浏览器,在弹出的Debug页面http://localhost:8081/debugger-ui后,选择开发着工具,点击手机上的按钮就可以看见浏览器控制台的
输出内容PressIn->longPress->pressOut;

二、手势响应生命周期
1.对于大部分应用,使用以上4个Touch*组件,在配合4个Press事件就能对用户的手势进行响应。但是对于比较复杂的交互,还得使用手势响应系统;
2.响应手势的基本单位是responder,具体来说就是View组件,任何View组件都是潜在的responder;
3.一个普通的View组件成为能响应手势操作的responder,只要设置几个手响应生命周期的方法即可,具体如下:
View.props.onStartShouldSetResponder:用户开始触摸屏幕的时候,是否愿意成为响应者;
View.props.onMoveShouldSetResponder:在每一个触摸点开始移动的时候,再询问一次是否响应触摸交互;
View.props.onResponderGrant:要开始响应触摸事件了;
View.props.onResponderReject:响应者现在另有其人,而且暂时不会放权,另作安排;
View.props.onResponderMove:用户正在屏幕上移动手指;
View.props.onResponderRelease:触摸操作结束收触发;
View.props.onResponderTerminationRequest:有其它组件请求接替响应者,当前View是否放权;
View.props.onResponderTerminate:响应权已经交出;
4.一个React Native应用中同时之能存在一个responder,具体响应步骤如下:
用户通过触摸或者滑动来“激活”某个responder,View.props.onStartShouldSetResponder以及View.props.onMoveShouldSetResponder这两个方法处理,如果返回true,则这个View能够响应触摸或者滑动手势被激活;
如果组件被激活,View.props.onResponderGrant方法调用,一般这个时候去改变组建的底色或者透明度,表示组件已经被激活;
接下来,用户开始滑动手指,此时View.props.onResponderMove方法调用
用户的手指离开屏幕之后,View.props.onResponderRelease方法调用,组件恢复被触摸之前样式,例如底色和透明度恢复之前的样式,完成一次手势操作;
正常流程:响应touch或者move手势 -> grant(被激活) -> move -> release(结束事件);

5.下面我们以实例演示下,相关代码如下:
index.android.js文件
import React,{
   … … 
} from 'react-native';

var AwesomeProject = React.createClass({
  getinitialState(){
    return {
      bg: 'white'
    };
  },componentwillMount(){
    this._gestureHandlers = {
      onStartShouldSetResponder: () => true,onMoveShouldSetResponder: ()=> true,onResponderGrant: ()=>{
        this.setState({bg: 'red'})
      },onResponderMove: ()=>{
        console.log(123)
      },onResponderRelease: ()=>{
        this.setState({bg: 'white'})
      }
    }
  },render() {
    return(
      <View style={styles.container}>
        <View {...this._gestureHandlers} style={[styles.rect,{backgroundColor:this.state.bg}]}></View>
      </View>
    );
  }
});
var styles = StyleSheet.create({
     … … 
});
AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
6.运行效果如下,onClick为未点击,Click为点击,后面为控制台输出log:


注意:如果运行Demode的时候错误提示如下图:

使用var AwesomeProject = React.createClass();的方式创建组件;
三、手势事件传递
1.onStartShouldSetResponder于onMoveShouldSetResponder是以冒泡的形式调用的,即嵌套最深的节点最先调用
2.意味着多个View同时在ShouldSetResponder中返回true时,最底层的View将优先”夺权“;
3.但是有些时候,某个父View会希望先能成为响应者,我们可以利用”捕获期“来解决。响应系统从最底层的组件开始冒泡前,会首先执行一个”捕获期“,在此期间会触发on*ShouldSetResponderCapture系列事件。因此,如果某个父View想要在触摸开始时阻止组件成为响应者,那就应该处理onStartShouldSetResponderCapture事件冰返回true值;
View.props.onStartShouldSetResponderCapture:(evt)=>true;
View.props.onMoveShouldSetReponderCapture(evt)=>ture;

4.下面我们将以实例演示一下,实现代码如下:
Index.android.js文件
import React,{
     ... ... 
} from 'react-native';

var AwesomeProject = React.createClass({
  getinitialState(){
    return {
      bg: 'white',bg2: 'white'
    }
  },componentwillMount(){
    this._gestureHandlers = {
      //外部正方形在“捕获期”阻止底层时间成为响应者
      onStartShouldSetResponderCapture: () => true,onMoveShouldSetResponderCapture: ()=> true,onResponderGrant: ()=>{this.setState({bg: 'red'})},onResponderMove: ()=>{console.log(123)},onResponderRelease: ()=>{this.setState({bg: 'white'})},}
    this._gestureHandlers2 = {
      //内部正方形在即时实现了on*ShouldSetResponder也无法成为响应者
      onStartShouldSetResponder: () => true,onResponderGrant: ()=>{this.setState({bg2: 'green'})},onResponderRelease: ()=>{this.setState({bg2: 'white'})}
    }
  },render: function() {
    return (
      <View style={styles.container}>
        <View {...this._gestureHandlers} style={[styles.rect,{"backgroundColor": this.state.bg}]}>
          <View {...this._gestureHandlers2} style={[styles.rect2,{"backgroundColor": this.state.bg2}]}>
          </View>
        </View>
      </View>
    );
  }
});
var styles = StyleSheet.create({
  ... ... 
});
AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
5.运行效果如下图,点击内部正方形,外部正方形相应事件:
四、evt参数
1.和Web开发中的事件参数类似,以上的每个方法都有一个evt参数,在事件发生的过程中,这个evt参数的nativeEvent属性的各个指能表示手势的状态:
nativeEventchangedtouches:在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
identifier:触摸点的ID
locationX:触摸点相对于父元素的横坐标
locationY:触摸点相对于父元素的纵坐标
pageX:触摸点相对于根元素的横坐标
pageY:触摸点相对于根元素的纵坐标
target:触摸点所在的元素ID
timestamp:触摸事件的时间戳,可用于移动速度的计算
touches:当前屏幕上的所有触摸点的集合

五、PanResponder
1.除了手势相应系统之外,React Native还抽象出一套PanResponder方法,它的抽象成程度更高,使用起来更为方便;
2.使用PanResponder的时候,相应手势的逻辑和流程都不变,只需要根据文档对几个方法名称参数修改即可:
一个参数evt;
第二个gestureState,包含手势进行过程中更多信息,比较常见如下:
dx/dy:手势进行到现在的横向/纵向相对位移;
vx/vy:此刻的横向/纵向速度;
numberActivetouches:reponder上的触摸的个数;
3.下面我们就使用PanResponder实现拖拽效果代码实现如下:
index.android.js文件
import React,{
  ... ... 
  PanResponder,} from 'react-native';


var AwesomeProject = React.createClass({
  getinitialState(){
    return {
      bg: 'white',top: 0,left: 0
    }
  },componentwillMount(){
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,onMoveShouldSetPanResponder: ()=> true,onPanResponderGrant: ()=>{
        //滑动开始时,获取矩形的左上坐标,并设置背景为红色
        this._top = this.state.top
        this._left = this.state.left
        this.setState({bg: 'red'})
      },onPanResponderMove: (evt,gs)=>{
        console.log(gs.dx+' '+gs.dy)
        //随着手势滑动,相应的改变矩形的位置
        this.setState({
          top: this._top+gs.dy,left: this._left+gs.dx
        })
      },onPanResponderRelease: (evt,gs)=>{
        //活动结束后,还原背景为白色
        this.setState({
          bg: 'white',top: this._top+gs.dy,left: this._left+gs.dx
        })}
    })
  },render: function() {
    return (
      <View style={styles.container}>
        //设置手势事件处理对象
        <View{...this._panResponder.panHandlers} style={[styles.rect,{
            "backgroundColor": this.state.bg,"top": this.state.top,"left": this.state.left}]}>
        </View>
      </View>
    );
  }
});
var styles = StyleSheet.create({
  container: {
    flex: 1,//开始的矩形位于中间,下图拖动到下部区域
    justifyContent: 'center',rect: {
    ... ... 
  }
});
AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
4.运行演示如下,由于只能上传图片,无法演示动画,样式中矩形位于中间,图中拖动至下方:

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom