ReactNative 仿QQ滑动删除组件详解

仿QQ滑动组件

    当业务中需要,实现类似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
    }
});

重点内容分以下模块

View组件获取手势的处理
            {...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组件滑动更新UI的问题
//手势移动的监听
    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)
    };
View组件滑动之后,点击收回的问题
//手势抬起的监听
    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
    };
View组件滑动之后,点击事件的处理

    我们会遇到,我们滑动出删除按钮之后,点击组件,组件会收缩回去,收缩之后点击组件相应的点击事件, 在这个问题上我们的处理方式需要进一步的处理

        if (!this.hasClick && Math.abs(changeX) < 5) {//5 是我们设定的一个最小值,可以修改
            this.props.onItemClick(index, id);
            return;
        }

结束语

    通过去封装RN组件,我们会发现,无论是什么语言,最重要的是我们要掌握思想,掌握技术的设计模式,才能更好的去做事情.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值