去年公司做了一个基于RN的手机端做题项目,其中产品提了一个需求是滑动切换上下题,一开始想着用轮播去实现,找了几个第三方插件写了个demo测试下发现不是很合适,究其原因有两个:
1、一进去需要加载比较多的题目,用户体验不好,网络不好经常卡顿。
2、当题目数多的时候,会爆内存。
当然可以考虑不用一开始就加载全部,动态加载的形式,不过由于是第三方插件,在没有提供相应方法的时候懒得去看源码修改,所以后面考虑自己实现。
在谷哥度娘和官方文档的帮助下,找到了一个叫做Gesture Responder System的东西(具体参考https://cloud.tencent.com/developer/section/1373055 )
这边提供一个例子,具体代码如下:
componentWillMount() { //原理是通过第一次触摸的x坐标和最后一次触摸的x坐标,判断距离是否大于50,大于则认为触发动作 this._gestureHandlers = { onStartShouldSetResponder: () => false, //对触摸进行响应 onMoveShouldSetResponder: () => true, //对滑动进行响应 onResponderTerminationRequest: () => true,//其他组件申请触摸事件时,是否允许释放当前对该组件的触摸事件 onResponderGrant: (evt) => {//激活时做的动作 this.start = evt.nativeEvent.locationX; }, onResponderRelease: (evt) => {//动作释放后做的动作 let end = evt.nativeEvent.locationX; let distance = end - this.start; console.log('滑动距离' + distance); if (distance > 50) { //下一题 let count = this.state.count; this.setState({ count: count + 1 }); } else if (distance < -50) { //上一题 let count = this.state.count; this.setState({ count: count - 1 }); } }, } }
上述的this._gestureHandlers即为主要的处理函数,然后在view中这样使用(注意{…this._gestureHandlers})
render() {
return (
<View {…this._gestureHandlers} style={styles.container}>
<TouchableOpacity onPress={this._onPress}>
<Text style={styles.welcome}>点击文字进行滑动</Text>
</TouchableOpacity>
<Text style={styles.instructions}>{this.state.count}</Text>
<Text style={styles.instructions}>{this.state.msg}</Text>
</View>
);
}
模拟器测试了下,完美~但是后面真机测试的时候出问题了,上面的TouchableOpacity始终点击不了,原因是因为模拟器使用的是鼠标,都是单点操作,真机不同,手机触摸的时候是多点的,造成监听函数以为当前是滑动,但是实际上用户只是想要点击,so~继续修改
componentWillMount() {
this._gestureHandlers = PanResponder.create({
//对触摸进行响应,onStartShouldSetPanResponder和onStartShouldSetPanResponderCapture两者有个为true则对触摸进行响应
//先拦截onStartShouldSetPanResponderCapture后拦截onStartShouldSetPanResponder
//只有在触摸拦截返回false的情况下,才会去拦截onMoveShouldSetPanResponderCapture和onMoveShouldSetPanResponder
//onStartShouldSetPanResponder: (e, gestureState) => { return false; },
//onStartShouldSetPanResponderCapture: (e, gestureState) => { return false; },
//对滑动进行响应
//拦截顺序是先onMoveShouldSetPanResponderCapture后onMoveShouldSetPanResponder
//onMoveShouldSetPanResponder: (e, gestureState) => { return false; },
onMoveShouldSetPanResponderCapture: (e, gestureState) => {
if (Math.abs(gestureState.dx) > 50) {
this.distance = gestureState.dx;
return true;
} else {
return false;
}
},
//onPanResponderGrant: () => { },//触摸(滑动)开始时
onPanResponderRelease: () => {//触摸(滑动)结束时
if (this.distance > 50) {
//下一题
let count = this.state.count;
this.setState({ count: count + 1 });
} else if (this.distance < -50) {
//上一题
let count = this.state.count;
this.setState({ count: count - 1 });
}
},
//其他组件申请触摸事件时,是否允许释放当前对该组件的触摸事件
onPanResponderTerminationRequest: () => true,
});
}
然后将{…this._gestureHandlers}改成{…this._gestureHandlers.panHandlers}
测试了下,完美!