How To Drag and Drop with Multiple View in React Native?

前提是你 提交于 2019-12-03 19:19:27

问题


I have added multiple view in the dynamically at same position also added the pan Gesture each of the views. The all view at the same position so that it override. My problem is that when wants to drag the last view, but it's dragged all views. How can drag the individual view.

Here you can see GIF in which all views. View1 , View 2 and View 3. All are dragging. I want to only view 3 is draggable, others drags after completion of drag 3.

1. Drag 3 - First drag.

2. Drag 2 - Second drag.

3. Drag 1 - Third drag.

Code :

import React, { Component } from 'react';
import {
  StyleSheet,
  View,
  Text,
  PanResponder,
  Animated,
  Easing,
  Dimensions,
  Platform,
  TouchableOpacity,
} from 'react-native';

let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');
const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
  constructor(props){
    super(props);

    this.state = {
        showDraggable   : true,
        dropZoneValues  : null,
        pan             : new Animated.ValueXY(),
        dataDrag        : [1,2,3,4],
    };

    this.panResponder = PanResponder.create({
        onStartShouldSetPanResponder    : () => true,
        onPanResponderMove              : Animated.event([null,{
            dx  : this.state.pan.x,
            dy  : this.state.pan.y
        }]),
        onPanResponderRelease           : (e, gesture) => {
            if(this.isDropZone(gesture)){
                this.setState({
                    showDraggable : false
                });
            }else{
                Animated.spring(
                    this.state.pan,
                    {toValue:{x:0,y:0}}
                ).start();
            }
        }
    });
}

isDropZone(gesture){
    var dz = this.state.dropZoneValues;
    return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
}

setDropZoneValues(event){
    this.setState({
        dropZoneValues : event.nativeEvent.layout
    });
}

render(){
    return (
        <View style={styles.mainContainer}>
            <View
                onLayout={this.setDropZoneValues.bind(this)}
                style={styles.dropZone}>
                <Text style={styles.text}>Drop me here!</Text>
            </View>

            {this.state.dataDrag.map((d, index) => (
              <View key = {index} style={styles.draggableContainer}>
                  <Animated.View
                      {...this.panResponder.panHandlers}
                      style={[this.state.pan.getLayout(), styles.circle]}>
                      <Text style={styles.text}>Drag {index}</Text>
                  </Animated.View>
              </View>
            ))}


        </View>
    );
}

renderDraggable(){
    //if(this.state.showDraggable){
        return (
            <View style={styles.draggableContainer}>
                <Animated.View
                    {...this.panResponder.panHandlers}
                    style={[this.state.pan.getLayout(), styles.circle]}>
                    <Text style={styles.text}>Drag me!</Text>
                </Animated.View>
            </View>
        );
    //}
  }
}

let styles = StyleSheet.create({
    mainContainer: {
        flex    : 1
    },
    dropZone    : {
        height  : 100,
        backgroundColor:'#2c3e50'
    },
    text        : {
        marginTop   : 25,
        marginLeft  : 5,
        marginRight : 5,
        textAlign   : 'center',
        color       : '#fff'
    },
    draggableContainer: {
        position    : 'absolute',
        top         : Window.height/2 - CIRCLE_RADIUS,
        left        : Window.width/2 - CIRCLE_RADIUS,
    },
    circle      : {
        backgroundColor     : '#1abc9c',
        width               : CIRCLE_RADIUS*2,
        height              : CIRCLE_RADIUS*2,
        borderRadius        : CIRCLE_RADIUS
    },
});

GIF :

I Want to below result:


回答1:


There are several places need to be modified to make it work.

Step by Step:

  1. Your Four circles holds their own position. So 4 Animated.ValueXY is required.

    this.dataDrag = [1,2,3,4];
    this.pan = this.dataDrag.map( () => new Animated.ValueXY() );
    
  2. Your PanResponder need information from current index. Pull it out as a function that returns function including info of index.

    getPanResponder(index) {
        return PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onPanResponderMove: Animated.event([null,{
                dx: this.pan[index].x,
                dy: this.pan[index].y
            }]),
            onPanResponderRelease : (e, gesture) => {
                if(this.isDropZone(gesture)){
                    this.setState({
                        showDraggable : false
                    });
                }else{
                    Animated.spring(
                        this.pan[index],
                        {toValue:{x:0,y:0}}
                    ).start();
                }
            }
        });    
    }
    
  3. Make your style according to above change. Removes outer view, which is not necessary, and also blocking events.

    {this.dataDrag.map((d, index) => (
        <Animated.View
            key={index}
            {...this.getPanResponder(index).panHandlers}
            style={[styles.draggableContainer, this.pan[index].getLayout(), styles.circle]}>
            <Text style={styles.text}>Drag {index}</Text>
        </Animated.View>
    ))}
    
  4. Do margin trick to reduce calculation of position. Change top / left to marginTop / marginLeft.

    draggableContainer: {
        position    : 'absolute',
        marginTop   : Window.height/2 - CIRCLE_RADIUS,
        marginLeft  : Window.width/2 - CIRCLE_RADIUS,
    },
    

Final Code:

import React, { Component } from 'react';
import {
    StyleSheet,
    View,
    Text,
    PanResponder,
    Animated,
    Easing,
    Dimensions,
    Platform,
    TouchableOpacity,
} from 'react-native';

let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');

export class App extends Component<{}> {
    constructor(props){
        super(props);

        this.dataDrag = [1,2,3,4];
        this.pan = this.dataDrag.map( () => new Animated.ValueXY() );

        this.state = {
            showDraggable   : true,
            dropZoneValues  : null,
        };
    }

    getPanResponder(index) {
        return PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onPanResponderMove              : Animated.event([null,{
                dx  : this.pan[index].x,
                dy  : this.pan[index].y
            }]),
            onPanResponderRelease           : (e, gesture) => {
                if(this.isDropZone(gesture)){
                    this.setState({
                        showDraggable : false
                    });
                }else{
                    Animated.spring(
                        this.pan[index],
                        {toValue:{x:0,y:0}}
                    ).start();
                }
            }
        });    
    }

    isDropZone(gesture){
        var dz = this.state.dropZoneValues;
        return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
    }

    setDropZoneValues(event){
        this.setState({
            dropZoneValues : event.nativeEvent.layout
        });
    }

    render(){
        return (
            <View style={styles.mainContainer}>
                <View
                    onLayout={this.setDropZoneValues.bind(this)}
                    style={styles.dropZone}>
                    <Text style={styles.text}>Drop me here!</Text>
                </View>

                {this.dataDrag.map((d, index) => (
                    <Animated.View
                        key={index}
                        {...this.getPanResponder(index).panHandlers}
                        style={[styles.draggableContainer, this.pan[index].getLayout(), styles.circle]}>
                        <Text style={styles.text}>Drag {index}</Text>
                    </Animated.View>
                ))}
            </View>
        );
    }
}

let styles = StyleSheet.create({
    mainContainer: {
        flex    : 1
    },
    dropZone    : {
        height  : 100,
        backgroundColor:'#2c3e50'
    },
    text        : {
        marginTop   : 25,
        marginLeft  : 5,
        marginRight : 5,
        textAlign   : 'center',
        color       : '#fff'
    },
    draggableContainer: {
        position    : 'absolute',
        marginTop         : Window.height/2 - CIRCLE_RADIUS,
        marginLeft        : Window.width/2 - CIRCLE_RADIUS,
    },
    circle      : {
        backgroundColor     : '#1abc9c',
        width               : CIRCLE_RADIUS*2,
        height              : CIRCLE_RADIUS*2,
        borderRadius        : CIRCLE_RADIUS
    },
});

Result:



来源:https://stackoverflow.com/questions/47551462/how-to-drag-and-drop-with-multiple-view-in-react-native

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!