react native滑动删除列表

简单封装的一个滑动弹出删除层效果的list组件。实现的比较粗暴,每个元素都添加了动画效果,这里应该可以进一步优化。不过rn感觉没有web开发灵活,比如获取指定组件(通过ref获取,比较麻烦),插入组件(暂时还没找到方法)实现起来比较困难。所以暂时还没想到优化的办法。

目前初学,所以很多地方可能用的不是最优的实现。后续了解到更好的办法会持续优化。

import React, { Component } from 'react';
import {
    Animated,
    StyleSheet,
    View,
    FlatList,
    PanResponder
} from 'react-native';

import md5 from 'crypto-js/md5';
import Button from './button/button';

/**
 * 滑动删除列表
    @param listStyle 列表样式
    @param shadowStyle 遮罩层样式
    @param shadowButtonImg 删除按钮图片
    @param shadowButtonImgStyle 删除按钮图片样式
    @param itemWidth 列表元素宽度
    @param listData 列表数据
    @param itemRender 列表元素render方法
    @param deleteItem 列表元素删除方法
    @param loadMore 列表加载更多方法
    @param refresh 列表刷新方法
    @param refreshing 列表刷新状态标示
 */
export default class SlideDeleteList extends Component {
    state = {
        pan:{},
        pan1:{},
        showResponder:{},
        hideResponder:{},
        listData:[],
        showFlag:false,
    };
    dataPhoto = [];

    constructor(props) {
        super(props);
        if(!this.props.itemWidth) {
            throw 'SlideDeleteList缺少参数 itemWidth(列表元素宽度)';
        }
        if(!this.props.listData) {
            throw 'SlideDeleteList缺少参数 listData(列表数据)';
        }
        if(!this.props.itemRender || typeof this.props.itemRender != 'function') {
            throw 'SlideDeleteList缺少参数 itemRender(列表元素render方法)';
        }
        if(!this.props.deleteItem || typeof this.props.deleteItem != 'function') {
            throw 'SlideDeleteList缺少参数 deleteItem(列表元素删除方法)';
        }
        this.processData(props.listData, true);
    }

    _keyExtractor = (item, index) => item.__id;

    getShadowShowResponder(id) {
        return {
            onStartShouldSetPanResponder: () => false,
            onMoveShouldSetPanResponder:  (evt, gestureState) => {
                if(gestureState.dx < -10 && !this.state.showFlag) {
                    return true;
                }else {
                    return false;
                }
            },
            onPanResponderGrant: (evt, gestureState) => {},
            onPanResponderMove: (evt, gestureState) => {
                this.state.pan[id].setValue(gestureState.dx);
            },
            onPanResponderRelease: (evt, gestureState) => {
                Animated.timing(this.state.pan[id],{
                    toValue:-this.props.itemWidth,
                }).start();
                this.state.showFlag = true;
            },
            onPanResponderTerminate: (evt, gestureState) => {
                Animated.timing(this.state.pan[id],{
                    toValue:-this.props.itemWidth,
                }).start();
                this.state.showFlag = true;
            },
        }
    }

    getShadowHideResponder(id) {
        return {
            onStartShouldSetPanResponder: () => false,
            onMoveShouldSetPanResponder:  (evt, gestureState) => {
                if(gestureState.dx > 10) {
                    return true;
                }else {
                    return false;
                }
            },
            onPanResponderGrant: (evt, gestureState) => {},
            onPanResponderMove: (evt, gestureState) => {
                this.state.pan[id].setValue(gestureState.dx-this.props.itemWidth);
            },
            onPanResponderRelease: (evt, gestureState) => {
                Animated.timing(this.state.pan[id],{
                    toValue:0,
                }).start();
                this.state.showFlag = false;
            },
            onPanResponderTerminate: (evt, gestureState) => {
                Animated.timing(this.state.pan[id],{
                    toValue:0,
                }).start();
                this.state.showFlag = false;
            },
        }
    }

    componentWillReceiveProps(newProps) {
        this.processData(newProps.listData);
    }

    processData(sourceData, init = false) {
        let listData = [],
            pan = this.state.pan,
            pan1 = this.state.pan1,
            showResponder = this.state.showResponder,
            hideResponder = this.state.hideResponder;
        let newDataPhoto = [];
        for(let i = 0; i < sourceData.length; i++) {
            let item = Object.assign({},sourceData[i]);
            item.__id = md5(JSON.stringify(item)).toString();
            listData.push(item);
            newDataPhoto.push(item.__id);
            if(this.dataPhoto.indexOf(item.__id) == -1) {
                pan[item.__id] = new Animated.Value(0);
                pan1[item.__id] = new Animated.Value(0);
                showResponder[item.__id] = PanResponder.create(this.getShadowShowResponder(item.__id));
                hideResponder[item.__id] = PanResponder.create(this.getShadowHideResponder(item.__id));
            }
        }
        this.dataPhoto = newDataPhoto;
        if(init) {
            this.state.listData = listData;
            this.state.pan = pan;
            this.state.pan1 = pan1;
            this.state.showResponder = showResponder;
            this.state.hideResponder = hideResponder;
            this.state.showFlag = false;
        } else {
            this.setState({
                listData : listData,
                pan : pan,
                pan1 : pan1,
                showResponder : showResponder,
                hideResponder : hideResponder,
                showFlag : false,
            });
        }
    }

    deleteItem(item) {
        Animated.timing(
            this.state.pan1[item.__id],
            {
                toValue:-this.props.itemWidth,
            },
        ).start(()=>this.props.deleteItem(item));
    }

    loadMore() {
        if(typeof this.props.loadMore == 'function') {
            this.props.loadMore();
        }
    }

    refresh() {
        if(typeof this.props.loadMore == 'function') {
            this.props.refresh();
        }
    }

    shadowRender(item) {
        return (
            <View style={[styles.list_shadow,this.props.shadowStyle]}>
                <Button 
                    onPress={this.deleteItem.bind(this,item)} 
                    imageStyle={[styles.list_shadow_button_img,this.props.shadowButtonImgStyle]} 
                    source={this.props.shadowButtonImg||Icon.delete} />
            </View>
        );
    }

    renderItem(item) {
        let _item = item.item;
        return (
            <Animated.View 
                {...this.state.showResponder[_item.__id].panHandlers}
                style={{
                    width:this.props.itemWidth,
                    flexDirection:"row",
                    overflow:"hidden",
                    transform:[{translateX:this.state.pan1[_item.__id]}],
                }}>
                {this.props.itemRender(_item)}
                <Animated.View
                    {...this.state.hideResponder[_item.__id].panHandlers}
                    style={{
                        transform:[{translateX:this.state.pan[_item.__id]}],
                    }}>
                    {this.shadowRender(_item)}
                </Animated.View>
            </Animated.View>
        );
    }

    render() {
        return (
            <View style={[styles.list,this.props.listStyle]}>
                <FlatList
                    data={this.state.listData}
                    keyExtractor={this._keyExtractor}
                    onEndReachedThreshold={0.1}
                    renderItem={this.renderItem.bind(this)}
                    onEndReached={this.loadMore.bind(this)}
                    onRefresh={this.refresh.bind(this)}
                    refreshing={this.props.refreshing}
                />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    list: {
        flex: 1,
        alignItems:'center',
    },
    list_shadow: {
        backgroundColor:'#373F48',
        width:100,
        height:50,
        opacity:0.6,
        borderRadius:10,
        marginTop:8,
        alignItems:'center',
        justifyContent:'center',
    },
    list_shadow_button_img: {
        width:34,
        height:34,
    },
});

 

转载于:https://my.oschina.net/u/1772981/blog/1842882

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值