panResponder详解及Demo

来源:互联网 发布:淘宝卖家不发货怎么办? 编辑:程序博客网 时间:2024/06/05 09:58

我们知道,react-native封装了一系列的组件例如<Touchable**>来提供触摸事件的反馈,另外ButtonText等组件也提供了简单的点击方法来给组件快速添加触摸事件。但是这些组件都是针对某个特定的效果,因其是定制的,所以可自定义、调整的部分就会很少,至多是效果有个选项。那么,如果我们希望做复杂的效果,改怎么办呢?答案是panResopner

先看下panResponder的说明:

它可以将多点触摸操作协调成一个手势。它使得一个单点触摸可以接受更多的触摸操作,也可以用于识别简单的多点触摸手势。 
它提供了一个对触摸响应系统响应器的可预测的包装。对于每一个处理函数,它在原生事件之外提供了一个新的gestureState对象。

触摸整体流程

简单来说,就是它会将触摸中发生的每次事件,每次状态的转换都通过api提供出来,供开发者做深入的开发和操作。这里的状态和过程包括:

  1. 要求成为响应者

    onStartShouldSetPanResponder: (evt, gestureState) => true,onStartShouldSetPanResponderCapture: (evt, gestureState) => true,onMoveShouldSetPanResponder: (evt, gestureState) => true,onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
    • 1
    • 2
    • 3
    • 4
  2. 开始手势操作

    onPanResponderGrant: (evt, gestureState) => {}
    • 1
  3. 触摸点移动

    onPanResponderMove: (evt, gestureState) => {} 
    • 1
  4. 用户放开了所有的触摸点,且此时视图已经成为了响应者

    onPanResponderTerminationRequest: (evt, gestureState) => true,onPanResponderRelease: (evt, gestureState) => {}
    • 1
    • 2
  5. 另一个组件已经成为了新的响应者,所以当前手势将被取消

    onPanResponderTerminate: (evt, gestureState) => {}
    • 1
  6. 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者(暂只支持android)

    onShouldBlockNativeResponder: (evt, gestureState) => {    return true;   }
    • 1
    • 2
    • 3

整体的流程图是:

触摸事件参数

从上面的函数就可以看到,触摸事件中,基本都包含这两个参数:

  1. nativeEvent

    • changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
    • identifier - 触摸点的ID
    • locationX - 触摸点相对于父元素的横坐标
    • locationY - 触摸点相对于父元素的纵坐标
    • pageX - 触摸点相对于根元素的横坐标
    • pageY - 触摸点相对于根元素的纵坐标
    • target - 触摸点所在的元素ID
    • timestamp - 触摸事件的时间戳,可用于移动速度的计算
    • touches - 当前屏幕上的所有触摸点的集合
  2. gestureState

    • stateID - 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。
    • moveX - 最近一次移动时的屏幕横坐标
    • moveY - 最近一次移动时的屏幕纵坐标
    • x0 - 当响应器产生时的屏幕坐标
    • y0 - 当响应器产生时的屏幕坐标
    • dx - 从触摸操作开始时的累计横向路程
    • dy - 从触摸操作开始时的累计纵向路程
    • vx - 当前的横向移动速度
    • vy - 当前的纵向移动速度
    • numberActiveTouches - 当前在屏幕上的有效触摸点的数量

这里先给出定义,具体的每个参数的时候,会在讲触摸move的时候一起讲

给组件添加触摸事件

给一个组件添加触摸事件很简单,有两步:

  1. 创建一个panResponder
  2. 将panResponder的panHandles传给这个组件

看下代码:

import React, {PureComponent, Component} from 'react';import {    AppRegistry,    StyleSheet,    Text,    View,    PanResponder,} from 'react-native';export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            redViewBgColor: 'red',        }    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({redViewBgColor: 'red'})    }    _highlight(){        this.setState({redViewBgColor: 'blue'})    }    render() {        return (            <View style={styles.container}>                <View style={[styles.redView,{backgroundColor: this.state.redViewBgColor}]}                    {...this._panResponder.panHandlers}                ></View>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,    },    redView: {        width: 100,        height: 100,        marginTop: 100,        marginLeft: 100,    },});AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

onPanResponderGrant是当用户触摸到屏幕时,我们需要给用户一个反馈,让他知道,触摸已经起作用了。这里我们将view变蓝。

onPanResponderRelease是当用户触摸结束时,我们也要给个反馈,让用户知道触摸已经停止。这里我们将view变回红色。

之前记得看过,之所以native比web的效果用起来好很多,就是因为native的每个步骤,都会给用户反馈,让用户知道,他的行为已经有回应了。这也是我们在做前端包括交互设计的时候,非常需要注意的事情,要知道,用户是很不耐烦的,你一定要给用户一个不太需要思考的交互,不要让他去想诸如:‘我碰到这个按钮了吗?’,‘这个是这样用的吗?’类似的问题。感兴趣的可以看下一本书《Don’t make me think》。

效果如下:

触摸移动

nativeEvnet

locationX loactionY

我们试着在移动的时候,让view随着手指的移动而移动。这里主要需要处理onPanResponderMove函数,先试试用nativeEventlocationXlocationY来处理,代码如下:

import React, {PureComponent, Component} from 'react';import {    AppRegistry,    StyleSheet,    Text,    View,    PanResponder,} from 'react-native';export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,        }    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`locationX : ${evt.nativeEvent.locationX}   locationY : ${evt.nativeEvent.locationY}`);                this.setState({                        marginLeft: evt.nativeEvent.locationX,                        marginTop: evt.nativeEvent.locationY,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    render() {        return (            <View style={styles.container}>                <View style={[styles.redView,                    {                        backgroundColor: this.state.backgroundColor,                        marginTop: this.state.marginTop,                        marginLeft: this.state.marginLeft,                    }                ]}                    {...this._panResponder.panHandlers}                ></View>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,    },    redView: {        width: 100,        height: 100,    },});AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

在实际操作中,发现locationXlocationY的移动是非常跳跃的,和api上面的介绍并不一样。查了githubissues,发现很多人提locationXandroid上面不准确或者不变的bug,但这个问题没有人提,所以我就提了一个,地址是: When I use panResponder.nativeEvent.locationX and locationY, it changes queite strange

效果如下: 

pageX pageY

ok、根据介绍,pageX和pageY是触摸点相对于根元素的横纵坐标,其效果如何呢? 我们将上述代码中的locationX和locationY替换为pageX和pageY.代码:

import React, {PureComponent, Component} from 'react';import {    AppRegistry,    StyleSheet,    Text,    View,    PanResponder,} from 'react-native';export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,        }    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`pageX : ${evt.nativeEvent.pageX}   pageY : ${evt.nativeEvent.pageY}`);                this.setState({                        marginLeft: evt.nativeEvent.pageX,                        marginTop: pageY,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    render() {        return (            <View style={styles.container}>                <View style={[styles.redView,                    {                        backgroundColor: this.state.backgroundColor,                        marginTop: this.state.marginTop,                        marginLeft: this.state.marginLeft,                    }                ]}                    {...this._panResponder.panHandlers}                ></View>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,    },    redView: {        width: 100,        height: 100,    },});AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

reload下。看下效果:

这里发现,移动开始的时候,pageX和pageY其实会有个跳跃,但是在移动的过程中,其变化比较稳定。这里打了下log,发现本来是(100,100)的View,在点击的时候,(pageX,pageY)为(176,156),研究了下发现是因为我的触摸点相对于view其实是有距离的,所以造成了开始的跳跃。

接下里,我们在这个redView的外围增加一个view,看下获得的pageX和pageY是什么,代码:

export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,        }    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`locationX : ${evt.nativeEvent.pageX}   locationY : ${evt.nativeEvent.pageY}`);                this.setState({                    marginLeft: evt.nativeEvent.pageX,                    marginTop: evt.nativeEvent.pageY,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    render() {        return (            <View style={styles.container}>                <View style={{height: 200,width: 200,backgroundColor:"grey"}}>                    <View style={[styles.redView,                        {                            backgroundColor: this.state.backgroundColor,                            marginTop: this.state.marginTop,                            marginLeft: this.state.marginLeft,                        }                    ]}                          {...this._panResponder.panHandlers}                    ></View>                </View>            </View>        );    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

效果: 

这里可以看到,pageX和pageY确实是当前的屏幕坐标,和其父view没有关系

怎么解决跳跃的问题呢? 目前从nativeEvent中的参数来看,是没办法解决的,所以我们继续看下gesture中的参数。(后面会给出解决方案)

gesture

moveX moveY

这里先看下moveX moveY,定义是最近一次移动时的屏幕横坐标和纵坐标,从定义上讲,和nativeEvent中的pageX和pageY应该是一样的,我们看下demo。

代码:

export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,        }    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`gestureState.moveX : ${gestureState.moveX}   gestureState.moveY : ${gestureState.moveY}`);                this.setState({                    marginLeft: gestureState.moveX,                    marginTop: gestureState.moveY,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    render() {        return (            <View style={styles.container}>                    <View style={[styles.redView,                        {                            backgroundColor: this.state.backgroundColor,                            marginTop: this.state.marginTop,                            marginLeft: this.state.marginLeft,                        }                    ]}                          {...this._panResponder.panHandlers}                    ></View>            </View>        );    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

效果和pageX,pageY一样,这里就不贴出来截屏了。 
在有parentView的情况下,效果也是没有区别。

x0 y0

x0和y0定义写的很清楚:

x0 - 当响应器产生时的屏幕坐标 
y0 - 当响应器产生时的屏幕坐标

就是当响应器产生时,即可以理解为触摸开始时,触摸的屏幕坐标。所以一旦触摸产生,x0 和 y0其实不会变了,除非这次触摸release,产生下次触摸。

dx dy

dx - 从触摸操作开始时的累计横向路程 
dy - 从触摸操作开始时的累计纵向路程

这个定义写的也蛮清楚的,就是本次触摸的累积横向路程和纵向路程。嘿、还记得上面我们遇到的问题吗?pageX、pageY 和 moveX、moveY都会有的问题,第一次如果不是点击左上角来移动,第一会有点跳跃,因为我们不知道当前的触摸点相对于view的坐标,所以坐标会偏移一些。 
有了dx和dy,我们就可以想办法解决这个问题了。我们只要记住上次这个view的left和top,然后set的时候,增加移动的距离,就解决了移动跳跃的问题了,先上代码:

import React, {PureComponent, Component} from 'react';import {    AppRegistry,    StyleSheet,    Text,    View,    PanResponder,} from 'react-native';export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,        };        this.lastX = this.state.marginLeft;        this.lastY = this.state.marginTop;    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`gestureState.dx : ${gestureState.dx}   gestureState.dy : ${gestureState.dy}`);                this.setState({                    marginLeft: this.lastX + gestureState.dx,                    marginTop: this.lastY + gestureState.dy,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();                this.lastX = this.state.marginLeft;                this.lastY = this.state.marginTop;            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    render() {        return (            <View style={styles.container}>                    <View style={[styles.redView,                        {                            backgroundColor: this.state.backgroundColor,                            marginTop: this.state.marginTop,                            marginLeft: this.state.marginLeft,                        }                    ]}                          {...this._panResponder.panHandlers}                    ></View>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,    },    redView: {        width: 100,        height: 100,    },});AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

效果:

vx vy

接下来的参数是vx和vy,这里不详细介绍,给出定义:

vx - 当前的横向移动速度 
vy - 当前的纵向移动速度

控制触摸是否响应

上面一直在讲move的时候的用法, 下面看下如何控制是否响应

onStartShouldSetPanResponder

这个很简单了,返回true,怎变成响应器,否则不会响应.但是当我真的去demo 的时候,诡异的事情发生了,onStartShouldSetPanResponder return false,居然view还能移动。打断点看了下,原来是因为onMoveShouldSetPanResponder是true,那么其移动是正常的。所以onStartShouldSetPanResponder只控制当触摸开始时,不会执行onPanResponderGrant函数。但是如果发生了move,则还是会走onPanResponderGrant函数。

上面简介的时候,还有个onStartShouldSetPanResponderCapture没有介绍,这个我尝试了几种组合:

onStartShouldSetPanResponderonStartShouldSetPanResponderCapture结果truetrueworktruefalseworkfalsetrueworkfalsefalsenot work

这说明,只要两者有一个true,则触摸都会进入onPanResponderGrant。网上搜索了下,也没有找到两者的区别。说实话,我对这里还是表示疑惑的,如果有读者知道原因,请指教。我目前怀疑是react-native的bug

onMoveShouldSetPanResponder

onMoveShouldSetPanResponder 从字面上理解,也是是否可以在移动中响应。我们先将其返回false,看下效果。居然可以移动…

查了github,貌似是个bug [Touchable] How to stopPropagation touch event

还是好多bug啊,一声叹息。。。(还是我太蠢没找到???)

多层响应

我们先尝试,在红色区域下面加一个灰色的区域,而且实现上面的触摸移动效果: 
代码:

import React, {PureComponent, Component} from 'react';import {    AppRegistry,    StyleSheet,    Text,    View,    PanResponder,} from 'react-native';export default class TouchStartAndRelease extends PureComponent {    constructor(props) {        super(props);        this.state = {            backgroundColor: 'red',            marginTop: 100,            marginLeft: 100,            backgroundColor1: 'grey',            marginTop1: 100,            marginLeft1: 100,        };        this.lastX = this.state.marginLeft;        this.lastY = this.state.marginTop;        this.lastX1 = this.state.marginLeft1;        this.lastY1 = this.state.marginTop1;    }    componentWillMount(){        this._panResponder = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight();                console.log('highlight');            },            onPanResponderMove: (evt, gestureState) => {                console.log(`gestureState.dx : ${gestureState.dx}   gestureState.dy : ${gestureState.dy}`);                this.setState({                    marginLeft: this.lastX + gestureState.dx,                    marginTop: this.lastY + gestureState.dy,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight();                this.lastX = this.state.marginLeft;                this.lastY = this.state.marginTop;            },            onPanResponderTerminate: (evt, gestureState) => {            },        });        this._panResponder1 = PanResponder.create({            onStartShouldSetPanResponder: (evt, gestureState) => {                return true;            },            onMoveShouldSetPanResponder:  (evt, gestureState) => {                return true;            },            onPanResponderGrant: (evt, gestureState) => {                this._highlight1();            },            onPanResponderMove: (evt, gestureState) => {                console.log(`gestureState.dx : ${gestureState.dx}   gestureState.dy : ${gestureState.dy}`);                this.setState({                    marginLeft1: this.lastX1 + gestureState.dx,                    marginTop1: this.lastY1 + gestureState.dy,                });            },            onPanResponderRelease: (evt, gestureState) => {                this._unhighlight1();                this.lastX1 = this.state.marginLeft1;                this.lastY1 = this.state.marginTop1;            },            onPanResponderTerminate: (evt, gestureState) => {            },        });    }    _unhighlight(){        this.setState({            backgroundColor: 'red',        });    }    _highlight(){        this.setState({            backgroundColor: 'blue',        });    }    _unhighlight1(){        this.setState({            backgroundColor1: 'grey',        });    }    _highlight1(){        this.setState({            backgroundColor1: 'green',        });    }    render() {        return (            <View style={styles.container}>                <View style={                    [styles.greyView,                    {                        backgroundColor: this.state.backgroundColor1,                        marginTop: this.state.marginTop1,                        marginLeft: this.state.marginLeft1,                    }                    ]}                      {...this._panResponder1.panHandlers}                >                    <View style={[styles.redView,                        {                            backgroundColor: this.state.backgroundColor,                            marginTop: this.state.marginTop,                            marginLeft: this.state.marginLeft,                        }                    ]}                          {...this._panResponder.panHandlers}                    ></View>                </View>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,    },    greyView: {        width: 200,        height: 200,    },    redView: {        width: 100,        height: 100,    },});AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149

看下效果:

点击红色区域,其变蓝,然后移动开始之后,底部的灰色也会变绿,移动发现,整体移动,相当于移动下面的区域。松开手后,上面的view不变回红色,下面的view变回灰色.只有单独点上面的view,而不移动,松开后其才会转成变回红色。

这是为什么呢? 我们分别在两个view的move函数中打log,发现根本没有进入上面view的move函数。这时就要介绍下上面讲到的另一个函数了:onPanResponderTerminate,我们发现,移动的时候,进入了上面view的这个函数,说明它的控制权,被抢走了。rn的多层触摸事件,我理解是从底层挨个网上查询,看哪个view想要接收,一定被接收,则消息不再传递。

把红色view的onStartShouldSetPanResponder 和 onMoveShouldSetPanResponder都返回false,发现点击红色区域的时候,灰色的区域也会有效果,和我们正常的理解也一样。效果如下:

多点触摸

如果是多点触摸呢?这里就要通过nativeEvent里面的touches去处理。记得,当前活跃的panResponder只有一个,所以需要在一个responder里面取处理两个触摸。

本篇文章到底结束,这里我们简单介绍了panResponder的触摸事件各个阶段的处理,以及参数的使用。还是有很多的问题,如果有新的消息,我会udpate这篇blog。


转载http://blog.csdn.net/njafei/article/details/76551176

原创粉丝点击