美文网首页react-native
React Nativie 拖拽排序

React Nativie 拖拽排序

作者: 小雨TT | 来源:发表于2017-08-10 13:58 被阅读0次

    需求

    有一个图片上传的功能,选择完图片之后会按照选择顺序进行排序,系统会默认前5张为系统展示图片,其他的图片留存在系统内供运营商使用. 图片选择完之后可以进行拖拽,调整顺序.

    先上效果图

    GIF.gif

    1. 响应手势事件,绑定相关的方法.

        componentWillMount() {
        this._panResponder = PanResponder.create({
            //用户开始触摸屏幕的时候,是否愿意成为响应者;
            onStartShouldSetPanResponder: () => true,
            //用户开始触摸屏幕的时候,是否愿意成为响应者;
            onMoveShouldSetPanResponder: () => true,
            // 开始按下的时候 调用的方法.
            onPanResponderGrant: (evt, gestureState) => this.onPanResponderGrant(evt),
            // 手指移动的时候调用的方法
            onPanResponderMove: (evt, gestureState) => this.onPanResponderMove(evt, gestureState),
            // 手指放开的调用的方法.
            onPanResponderRelease: (evt, gestureState) => this.onPanResponderEnd(),
            onPanResponderTerminate: (evt, gestureState) => this.onPanResponderEnd()
    
        });
    }
    
      
     render() {
        const views1 = [];
        const views2 = [];
    
        this.names.forEach((value, index) => {
            let bg = this.color[index];
            if (index >= 5) {
                views2.push(
                    <View
                         // 加了这句 View 才能响应手势 
                        {...this._panResponder.panHandlers}
                        key={value}
                        style={
                            [styles.circle, {
                                left: (index - 5) * CIRCLE_SIZE,
                                backgroundColor: bg,
                                top: CIRCLE_SIZE,
                            }]}
                        ref={ref => this.names[index] = ref}
                    />)
            } else {
                views1.push(
                    <View
                        // 加了这句 View 才能响应手势
                        {...this._panResponder.panHandlers}
                        key={value}
                        style={
                            [styles.circle,
                                {left: index * CIRCLE_SIZE},
                                {backgroundColor: bg},
                            ]}
                        ref={ref => this.names[index] = ref}
                    />)
            }
        });
        return (
            <View
                style={styles.container}>
                {views1}
                {views2}
            </View>
        );
    }
    

    2 需要根据按下的位置来判断 要移动哪个view

      // 根据按下的坐标,判断索引
    getIdByPosition(pageX, pageY) {
        let index = -1;
        if (pageX > CIRCLE_SIZE * this.names.length) {
            index = this.names.length - 1;
        } else {
            index = Math.floor((pageX) / CIRCLE_SIZE)
        }
        // 如果触摸的高度(需要减去导航栏的高度)大于圆的半径,说明是第二行
        if (pageY - NAV_BAR_HEIGHT > CIRCLE_SIZE) {
            index += 5;
        }
        return index;
    }
    
    static getLeftValueYById(id) {
        return id >= 5 ? (id - 5) * CIRCLE_SIZE : id * CIRCLE_SIZE;
    }
    
    static getTopValueById(id) {
        return id >= 5 ? CIRCLE_SIZE : 0;
    }
    

    3. 开始按下的操作

    // 开始按下手势操作。加点偏移量给用户一些视觉反馈,让他们知道发生了什么事情!
    onPanResponderGrant(evt) {
        const {pageX, locationX, pageY, locationY} = evt.nativeEvent;
        this.index = this.getIdByPosition(pageX, pageY);
        console.log("this.index" + this.index);
        this.preX = pageX - locationX;
        this.preY = pageY - locationY;
        // 给退拽的item加个阴影
        let item = this.names[this.index];
        item.setNativeProps({
            style: {
                shadowColor: "red",
                shadowOpacity: 0.5,
                shadowRadius: 5,
                shadowOffset: {height: 10, width: 10},
                elevation: 5,
                top: -2,
            }
        });
    
    }
    

    3. 移动的操作, 移动的时候 需要根据手指移动的位置,来判断哪两个小球交换.

      // 开始移动
      onPanResponderMove(evt, gestureState) {
        //  按下的小球
        let index = this.index;
        // 按下的小球需要移动到哪
        let left = this.preX + gestureState.dx;
        let top = this.preY + gestureState.dy;
        // 根据ref 来获取索引对应的view,setNativeProps来设置小球的位置
        let item = this.names[this.index];
        item.setNativeProps({
            style: {
                left: left,
                top: top,
            }
        });
        // 手指移动的某个点对应的索引,如果手指移动到某个点对应的索引 与按下那个点对应的索引不同,就交换两个小球的位置.
        let collideIndex = this.getIdByPosition(evt.nativeEvent.pageX, evt.nativeEvent.pageY);
        if (collideIndex !== this.index && collideIndex !== -1) {
            let collideItem = this.names[collideIndex];
            collideItem.setNativeProps({
                style: {
                    left: ListViewPage.getLeftValueYById(this.index),
                    top: ListViewPage.getTopValueById(this.index)
                }
            });
    
            //交换两个值
            [this.names[this.index], this.names[collideIndex]] = [this.names[collideIndex], this.names[this.index]];
            [this.order[this.index], this.order[collideIndex]] = [this.order[collideIndex], this.order[this.index]];
            this.index = collideIndex;
        }
    }
    

    4. 手指释放的操作

       onPanResponderEnd() {
        // 复位
        const shadowStyle = {
            shadowColor: "#000",
            shadowOpacity: 0,
            shadowRadius: 0,
            shadowOffset: {height: 0, width: 0,},
            elevation: 0
        };
        // 这个索引是已经交换过的索引.
        let item = this.names[this.index];
        let index = this.index;
        let leftValue = ListViewPage.getLeftValueYById(this.index);
        let topValue = ListViewPage.getTopValueById(this.index);
    
        item.setNativeProps({
            style: {
                ...shadowStyle,
                left: leftValue,
                top: topValue,
            }
        });
    }
    

    5.全部的代码

        import React, {Component} from 'react';
        import {
            StyleSheet,
            View,
            PanResponder,
            Animated,
            LayoutAnimation,
            UIManager,
        } from 'react-native';
    
        /**
         * 自定义拖拽图片排序
         **/
    
        const CIRCLE_SIZE = 70;
        const NAV_BAR_HEIGHT = 0;
    
        export default class DragSort extends Component {
    
    constructor(props) {
        super(props);
    
        this.names = ['Android', 'iOS', 'js', 'jq', '00', '11', '22', '33', '44', '55'];
        this.order = ['Android', 'iOS', 'js', 'jq', '00', '11', '22', '33', '44', '55'];
        this.color = ['red', 'blue', 'black', 'pink', 'gray', '#03ef94', '#009688', '#607d8b', '#3f51b5', '#00796b'];
    
    
    }
    
    // 开始按下手势操作。加点偏移量给用户一些视觉反馈,让他们知道发生了什么事情!
    onPanResponderGrant(evt) {
        const {pageX, locationX, pageY, locationY} = evt.nativeEvent;
        this.index = this.getIdByPosition(pageX, pageY);
        console.log("this.index" + this.index);
        this.preX = pageX - locationX;
        this.preY = pageY - locationY;
        // 给退拽的item加个阴影
        let item = this.names[this.index];
        item.setNativeProps({
            style: {
                shadowColor: "red",
                shadowOpacity: 0.5,
                shadowRadius: 5,
                shadowOffset: {height: 10, width: 10},
                elevation: 5,
                top: -2,
            }
        });
    
    }
    
    // 最近一次的移动距离为gestureState.move{X,Y}
    onPanResponderMove(evt, gestureState) {
        let index = this.index;
        let left = this.preX + gestureState.dx;
        let top = this.preY + gestureState.dy;
        let item = this.names[this.index];
    
        item.setNativeProps({
            style: {
                left: left,
                top: top,
            }
        });
    
        let collideIndex = this.getIdByPosition(evt.nativeEvent.pageX, evt.nativeEvent.pageY);
    
    
        if (collideIndex !== this.index && collideIndex !== -1) {
            let collideItem = this.names[collideIndex];
            collideItem.setNativeProps({
                style: {
                    left: ListViewPage.getLeftValueYById(this.index),
                    top: ListViewPage.getTopValueById(this.index)
                }
            });
    
            //交换两个值
            [this.names[this.index], this.names[collideIndex]] = [this.names[collideIndex], this.names[this.index]];
            [this.order[this.index], this.order[collideIndex]] = [this.order[collideIndex], this.order[this.index]];
            this.index = collideIndex;
        }
    }
    
    
    // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
    // 一般来说这意味着一个手势操作已经成功完成。
    onPanResponderEnd() {
        // 复位
        const shadowStyle = {
            shadowColor: "#000",
            shadowOpacity: 0,
            shadowRadius: 0,
            shadowOffset: {height: 0, width: 0,},
            elevation: 0
        };
        let item = this.names[this.index];
        let index = this.index;
        let leftValue = ListViewPage.getLeftValueYById(this.index);
        let topValue = ListViewPage.getTopValueById(this.index);
    
        item.setNativeProps({
            style: {
                ...shadowStyle,
                left: leftValue,
                top: topValue,
            }
        });
    
        console.log(this.order);
    
    }
    
    // 根据按下的坐标,判断索引
    getIdByPosition(pageX, pageY) {
        let index = -1;
        if (pageX > CIRCLE_SIZE * this.names.length) {
            index = this.names.length - 1;
        } else {
            index = Math.floor((pageX) / CIRCLE_SIZE)
        }
        // 如果触摸的高度(需要减去导航栏的高度)大于圆的半径,说明是第二行
        if (pageY - NAV_BAR_HEIGHT > CIRCLE_SIZE) {
            index += 5;
        }
        return index;
    }
    
    static getLeftValueYById(id) {
        return id >= 5 ? (id - 5) * CIRCLE_SIZE : id * CIRCLE_SIZE;
    }
    
    static getTopValueById(id) {
        return id >= 5 ? CIRCLE_SIZE : 0;
    }
    
    
    
    componentWillMount() {
        this._panResponder = PanResponder.create({
            //用户开始触摸屏幕的时候,是否愿意成为响应者;
            onStartShouldSetPanResponder: () => true,
            //用户开始触摸屏幕的时候,是否愿意成为响应者;
            onMoveShouldSetPanResponder: () => true,
            // 开始触摸的时候 调用的方法.
            onPanResponderGrant: (evt, gestureState) => this.onPanResponderGrant(evt),
            // 手指移动的时候调用的方法
            onPanResponderMove: (evt, gestureState) => this.onPanResponderMove(evt, gestureState),
            // 手指放开的调用的方法.
            onPanResponderRelease: (evt, gestureState) => this.onPanResponderEnd(),
            onPanResponderTerminate: (evt, gestureState) => this.onPanResponderEnd()
    
        });
    }
    
    render() {
        const views1 = [];
        const views2 = [];
    
        this.names.forEach((value, index) => {
            let bg = this.color[index];
            if (index >= 5) {
                views2.push(
                    <View
                        {...this._panResponder.panHandlers}
                        key={value}
                        style={
                            [styles.circle, {
                                left: (index - 5) * CIRCLE_SIZE,
                                backgroundColor: bg,
                                top: CIRCLE_SIZE,
                            }]}
                        ref={ref => this.names[index] = ref}
                    />)
            } else {
                views1.push(
                    <View
                        {...this._panResponder.panHandlers}
                        key={value}
                        style={
                            [styles.circle,
                                {left: index * CIRCLE_SIZE},
                                {backgroundColor: bg},
                            ]}
                        ref={ref => this.names[index] = ref}
                    />)
            }
        });
        return (
            <View
                style={styles.container}>
                {views1}
                {views2}
            </View>
        );
    }
    

    }

    const styles = StyleSheet.create({
    container: {
    flex: 1
    },
    circle: {
    width: CIRCLE_SIZE,
    height: CIRCLE_SIZE,
    borderRadius: CIRCLE_SIZE / 2,
    backgroundColor: 'blue',
    position: 'absolute',
    }
    });

    相关文章

      网友评论

        本文标题:React Nativie 拖拽排序

        本文链接:https://www.haomeiwen.com/subject/icjhrxtx.html