写在开始之前的话,说好的一个星期一篇的牛逼已然破了,破了就破了吧,我们该坚持的东西,还是需要坚持,不是吗?那么开始吧...

Touchable**触摸控件

我们知道在react native中,如果想要设置点击事件的话,并不像android中那样,

可以直接设置View.setOnClickListener,而是需要通过触摸控件来完成添加点击事

件,需要通过以下Touchable**来设置:

TouchableHighlight,

TouchableNativeFeedback,

TouchableOpacity,

TouchableWithoutFeedback

他们的功能和使用方法基本类似,只是在Touch的时候反馈的效果不同,他们有以下几个回调方法:

onPressIn:点击开始

onPressOut:点击结束或离开

onPress:单击事件回调

onLongPress:长按事件回调

基本用法如下:

onPressIn={() => console.log("onPressIn")}

onPressOut={() => console.log("onPressOut")}

onPress={() => console.log("onPress")}

onLongPress={() => console.log("onLongPress")}

>

style={styles.button}

source={require('./img/rn_logo.png')} />

用法比较简单,这里不再介绍。下面我们了解一下给不能点击的组件添加点击事件呢?

我们知道普通的组件并不能设置点击事件,那是因为普通的组件,它并不是触摸响应者,而想成为触摸响应者的话就需要你申请。

第一步:申请

想要申请成为触摸着,需要通过下面两个方法:

View.props.onStartShouldSetResponder

这个属性接受一个回调函数,如果返回true就是申请成为触摸事件的响应者。

View.props.onMoveShouldSetResponder

这个属性接受一个回调函数,如果返回true就是申请成为滑动过程中的响应者。

在实际的代码中,我们通过设置PanResponder来申请和监听的触摸事件。

onStartShouldSetPanResponder: (evt, gestureState) => true,

onStartShouldSetPanResponderCapture: (evt, gestureState) => true,

onMoveShouldSetPanResponder: (evt, gestureState) => true,

onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

其中

onStartShouldSetPanResponderCapture: (evt, gestureState) => true,

是表示,是否成为事件的劫持者,如果返回是,则不会把事件传递给它的子元素

onStartShouldSetPanResponderCapture: (evt, gestureState) => true,

表示是否成为滑动事件的劫持者,如果返回是,则不会把滑动事件传递给它的子元素。

第二步:开始手势操作

onPanResponderGrant: (evt, gestureState) => {}

第三步:触摸点移动

onPanResponderMove: (evt, gestureState) => {}

第四步:用户放开了所有的触摸点,且此时视图已经成为了响应者

onPanResponderTerminationRequest: (evt, gestureState) => true,

onPanResponderRelease: (evt, gestureState) => {}

第五步:另一个组件已经成为了新的响应者,所以当前手势将被取消

onPanResponderTerminate: (evt, gestureState) => {}

第六步:返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者(暂只支持android)

onShouldBlockNativeResponder: (evt, gestureState) => {

return true;

}

图片

在上面的代码中,我们看到,在触摸开始到滑动,在到触摸结束的过程中,会返回两个参数,evt和gestureState,下面我们介绍一下这两个参数:

触摸事件的参数--nativeEvent

nativeEvent

changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)

identifier - 触摸点的ID

locationX - 触摸点相对于父元素的横坐标

locationY - 触摸点相对于父元素的纵坐标

pageX - 触摸点相对于根元素的横坐标

pageY - 触摸点相对于根元素的纵坐标

target - 触摸点所在的元素ID

timestamp - 触摸事件的时间戳,可用于移动速度的计算

touches - 当前屏幕上的所有触摸点的集合

gestureState

stateID - 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。

moveX - 最近一次移动时的屏幕横坐标

moveY - 最近一次移动时的屏幕纵坐标

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

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

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

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

vx - 当前的横向移动速度

vy - 当前的纵向移动速度

numberActiveTouches - 当前在屏幕上的有效触摸点的数量

我们看一个具体的例子

效果如图:

image

代码如下:

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 (

{

backgroundColor: this.state.backgroundColor,

marginTop: this.state.marginTop,

marginLeft: this.state.marginLeft,

}

]}

{...this._panResponder.panHandlers}

>

);

}

}

const styles = StyleSheet.create({

container: {

flex: 1,

},

redView: {

width: 100,

height: 100,

},

});

为了做到图中的组件可以随我们的手指的滑动而更换位置,我们需要实时的设置它的绝对位置,left和top,这里面用到了两个属性

gestureState.dx和gestureState.dx:

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

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

我们通过记录它的初始位置marginLeft和marginTop,然后加上上面的dx和dy,也就是它的累积滑动距离,就知道了它的实时位置,然后把它设置给View,就是上面我们想要的效果了。

在日历上添加横向滑动切换月份

这个是一开始我想要实现的效果,这里不弄gif图了,太麻烦。

componentWillMount() {

this._panResponder = PanResponder.create({

onStartShouldSetPanResponder: (evt, gestureState) => {

return true;

},

onMoveShouldSetPanResponder: (evt, gestureState) => {

return true;

},

onPanResponderGrant: (evt, gestureState) => {

},

onPanResponderMove: (evt, gestureState) => {

console.log(`gestureState.dx : ${gestureState.dx} gestureState.dy : ${gestureState.dy}`);

//上个月

if(gestureState.dx >100 && !this.state.isLoading){

this.setState({isLoading:true});

this.prevMonth();

//下个月

}else if(gestureState.dx < -100 && !this.state.isLoading){

this.setState({isLoading:true});

this.nextMonth();

}

},

onPanResponderRelease: (evt, gestureState) => {

this.setState({isLoading:false});

},

onPanResponderTerminate: (evt, gestureState) => {

},

});

}

当横向滑动的距离dx大于100时,就是向右滑动,我们就切换到上个月,其中isLoading用来判断是否正在刷新界面中。

距离dx小于-100时,就是向左滑,我们切换到下个月。

这里是View的代码

{...this._panResponder.panHandlers}

>

{this.state.year +'年'+ (this.state.month<10?'0'+this.state.month:this.state.month) +'月'+ (this.state.day<10?'0'+this.state.day:this.state.day) +'日'}

ref = 'CalendarMain'

onDatePickListener = {this.onDatePickListener.bind(this)}

onChangeDateListener = {this.onChangeDateListener.bind(this)}

year={this.state.year}

month={this.state.month}

day={this.state.day}

/>

我们通过{...this._panResponder.panHandlers}这句把PanResponder监听设置到想要设置的组件上,貌似不能设置在我们自定义的组件CalendarMain上,我当时试了一下,没有起作用。

学习react native 的手势识别学习了下面的两篇文章,写的都非常好,其中的代码部分是出自下面的第一篇文章,向作者表示感谢。

Over...

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐