当业务中需要,实现类似QQ滑动删除组件的时候,在Android或者IOS中,都存在封装好的组件,但是在RN中类似组件很少,如何使用RN去开发一个QQ滑动删除组件呢,这就是接下来要做的事情.
资源下载地址: https://download.csdn.net/download/gl_mine_csdn/11562495
最外层View组件 嵌套 一个View组件(一个View组件嵌套两个Text组件,并设置space-between) 一个位于上一个组件下面的TouchableWithoutFeedback组件.
监听当前滑动组件的onPanResponderMove方法,来控制组件的位置,然后显示出来删除按钮组件.
import React from 'react';
import {View, Text, StyleSheet, TouchableWithoutFeedback, PanResponder} from 'react-native';
import {deviceWidth, setSpText} from "../../utils/screenUtils"
import themeColor from "../../constants/themeColor";
export default class SwipeDeleteItem extends React.Component {
constructor(props) {
super(props);
this.state = {
translateX: 0,
};
this.pageXStart = null;
this.pageXEnd = null;
this.hasClick = false;
}
componentWillReceiveProps(nextProps) {
}
render() {
//this.props.list 外部传过来的 列表数据
//当前的数据结构
// let obj = {};
// let closeTime = "07:00";
// let openTime = "07:00";
// obj.closeName = "closeName";
// obj.openName = "openName";
// obj.closeTime = closeTime;
// obj.translateX = 0; //item 移动的距离
// obj.opacity = 0; //item 的透明度,因为每一次进入之后,红色的颜色闪烁
// obj.id = param.id;
// obj.delete = ()=>{}; //删除函数
let {translateX} = this.state;
let items = this.props.list.map((item, index) => {
return (
<View key={index}>
<View style={styles.containerStyle}>
<View style={[styles.contentStyle, {marginLeft: item.translateX}]}
onLayout={this._onLayout_View}//当前布局的layout数据
//PanResponder 重点中的重点 让该组件获取手势
{...PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
// 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
// gestureState.{x,y} 现在会被设置为0
this.handlerSeekGrant(evt, gestureState);
},
onPanResponderMove: (evt, gestureState) => {
// 最近一次的移动距离为gestureState.move{X,Y}
this.handlerSeekMove(evt, gestureState, index);
// 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
// 用户放开了所有的触摸点,且此时视图已经成为了响应者。
// 一般来说这意味着一个手势操作已经成功完成。
this.handlerSeekRelease(evt, gestureState, index, item.id);
},
onPanResponderTerminate: (evt, gestureState) => {
// 另一个组件已经成为了新的响应者,所以当前手势将被取消。
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
// 默认返回true。目前暂时只支持android。
return true;
},
}).panHandlers}>
<View>
<View style={styles.itemContentStyle}>
<Text>{item.openName}</Text>
<Text>{item.openTime}</Text>
</View>
<View style={[styles.itemContentStyle, {marginTop: setSpText(30)}]}>
<Text>{item.closeName}</Text>
<Text>{item.closeTime}</Text>
</View>
</View>
</View>
<TouchableWithoutFeedback
onLayout={this._onLayout_Delete_View}
onPress={() => item.delete()}>
<Text style={[styles.deleteStyle, {opacity: item.opacity}]}>{this.props.delete}</Text>
</TouchableWithoutFeedback>
</View>
</View>
)
});
return (
<View>
<Text style={styles.titleStyle}>{this.props.title}</Text>
{items.length > 0 ? items : null}
</View>
);
}
//整体布局的位置
_onLayout_View = (e) => {
let {x, y, width, height} = e.nativeEvent.layout;
console.log("_onLayout_View: x:" + x + ",y:" + y + ",width:" + width + ",height:" + height)
};
//删除布局的位置
_onLayout_Delete_View = (e) => {
let {x, y, width, height} = e.nativeEvent.layout;
console.log("_onLayout_Delete_View: x:" + x + ",y:" + y + ",width:" + width + ",height:" + height)
};
onPress = (evt) => {
console.log("onPress---->" + evt)
};
//手势第一次触摸的监听
handlerSeekGrant = (evt, gestureState) => {
let x = evt.nativeEvent.pageX;
this.pageXStart = x;
console.log("handlerSeekGrant: " + x)
};
//手势移动的监听
handlerSeekMove = (evt, gestureState, index) => {
let x = evt.nativeEvent.pageX;
let maxX = setSpText(67 * 2);
this.pageXEnd = x;
let changeX = this.pageXEnd - this.pageXStart;
let translateX = 0;
if (changeX < 0) {//左
translateX = Math.abs(changeX) > maxX ? -maxX : changeX;
}
// this.setState({
// translateX: translateX
// });
let op = 1;
this.props.updateItemTranslateX(translateX, index, op);
console.log("onResponderMove: " + x + " ,changeX: " + changeX + ",index:" + index, ",op:" + op)
};
//手势抬起的监听
handlerSeekRelease = (evt, gestureState, index, id) => {
let x = evt.nativeEvent.pageX;
let maxX = setSpText(67 * 2);
this.pageXEnd = x;
let changeX = this.pageXEnd - this.pageXStart;
let translateX = 0;
if (changeX < 0) {//左
translateX = Math.abs(changeX) > maxX / 2 ? -maxX : 0;
} else if (changeX > 0 || changeX === 0) {//右
translateX = 0;
}
// this.setState({
// translateX: translateX
// });
let op = translateX === 0 ? 0 : 1;
this.props.updateItemTranslateX(translateX, index, op);
console.log("onResponderRelease: x:" + x + ",index: " + index + ",changeX: " + changeX + ",op:" + op);
if (!this.hasClick && Math.abs(changeX) < 5) {//false
this.props.onItemClick(index, id);
return;
}
this.hasClick = (translateX === -maxX);//true false
};
}
//样式配置
const styles = StyleSheet.create({
containerStyle: {
width: deviceWidth,
flexDirection: "row",
marginTop: setSpText(32)
},
titleStyle: {
marginLeft: setSpText(32),
marginTop: setSpText(32)
},
contentStyle: {
width: deviceWidth,
backgroundColor: themeColor.white,
paddingTop: setSpText(40),
paddingBottom: setSpText(40),
paddingLeft: setSpText(30),
paddingRight: setSpText(30)
},
itemContentStyle: {
flexDirection: "row",
justifyContent: "space-between"
},
deleteStyle: {
backgroundColor: themeColor.color_FF3D33,
textAlign: "center",
lineHeight: setSpText(94 * 2),
height: setSpText(94 * 2),
width: setSpText(67 * 2),
color: themeColor.white,
position: "absolute",
zIndex: -1,
right: 0
}
});
{...PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
// 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
// gestureState.{x,y} 现在会被设置为0
this.handlerSeekGrant(evt, gestureState);
},
onPanResponderMove: (evt, gestureState) => {
// 最近一次的移动距离为gestureState.move{X,Y}
this.handlerSeekMove(evt, gestureState, index);
// 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
// 用户放开了所有的触摸点,且此时视图已经成为了响应者。
// 一般来说这意味着一个手势操作已经成功完成。
this.handlerSeekRelease(evt, gestureState, index, item.id);
},
onPanResponderTerminate: (evt, gestureState) => {
// 另一个组件已经成为了新的响应者,所以当前手势将被取消。
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
// 默认返回true。目前暂时只支持android。
return true;
},
}).panHandlers}
//手势移动的监听
handlerSeekMove = (evt, gestureState, index) => {
let x = evt.nativeEvent.pageX;
let maxX = setSpText(67 * 2);
this.pageXEnd = x;
let changeX = this.pageXEnd - this.pageXStart;
let translateX = 0;
if (changeX < 0) {//左
translateX = Math.abs(changeX) > maxX ? -maxX : changeX;
}
// this.setState({
// translateX: translateX
// });
let op = 1;
this.props.updateItemTranslateX(translateX, index, op);
console.log("onResponderMove: " + x + " ,changeX: " + changeX + ",index:" + index, ",op:" + op)
};
//手势抬起的监听
handlerSeekRelease = (evt, gestureState, index, id) => {
let x = evt.nativeEvent.pageX;
let maxX = setSpText(67 * 2);
this.pageXEnd = x;
let changeX = this.pageXEnd - this.pageXStart;
let translateX = 0;
if (changeX < 0) {//左
translateX = Math.abs(changeX) > maxX / 2 ? -maxX : 0;
} else if (changeX > 0 || changeX === 0) {//右
translateX = 0;
}
// this.setState({
// translateX: translateX
// });
let op = translateX === 0 ? 0 : 1;
this.props.updateItemTranslateX(translateX, index, op);
console.log("onResponderRelease: x:" + x + ",index: " + index + ",changeX: " + changeX + ",op:" + op);
if (!this.hasClick && Math.abs(changeX) < 5) {//false
this.props.onItemClick(index, id);
return;
}
this.hasClick = (translateX === -maxX);//true false
};
我们会遇到,我们滑动出删除按钮之后,点击组件,组件会收缩回去,收缩之后点击组件相应的点击事件, 在这个问题上我们的处理方式需要进一步的处理
if (!this.hasClick && Math.abs(changeX) < 5) {//5 是我们设定的一个最小值,可以修改
this.props.onItemClick(index, id);
return;
}
通过去封装RN组件,我们会发现,无论是什么语言,最重要的是我们要掌握思想,掌握技术的设计模式,才能更好的去做事情.