小编典典

如何在React Native中使用多视图进行拖放?

reactjs

我在同一位置动态添加了多个视图,还添加了
平移手势每个视图。所有视图位于同一位置,因此可以
覆盖。我的问题是,当要拖动最后一个视图时,却
拖动了所有视图。如何拖动单个视图。

在这里您可以看到所有视图中的GIF。View1,View 2和View3。所有都在
拖动。我只希望视图3是可拖动的,其他人则在
拖动3 完成后拖动。

1.拖动3-第一个拖动。

2.拖动2-第二次拖动。

3.拖动1-第三次拖动。

代码:
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
    },
});

阅读 418

收藏
2020-07-22

共1个答案

小编典典

有几个地方需要修改以使其起作用。

Step by Step:

  1. 您的四个圈子拥有自己的位置。因此Animated.ValueXY需要4 。
        this.dataDrag = [1,2,3,4];
    this.pan = this.dataDrag.map( () => new Animated.ValueXY() );
  1. PanResponder需要当前索引中的信息。将其拉出作为返回包含info信息的函数的函数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();
                }
            }
        });    
    }
  1. 根据上述变化制作样式。删除不必要的外部视图以及阻塞事件。
        {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>
    ))}
  1. 做保证金技巧以减少头寸的计算。更改top / left为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
    },
});
2020-07-22