react-native

React Nativie 拖拽排序

2017-08-10  本文已影响0人  小雨TT

需求

有一个图片上传的功能,选择完图片之后会按照选择顺序进行排序,系统会默认前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',
}
});

上一篇下一篇

猜你喜欢

热点阅读