如何在 ScrollView 项中绑定拖动视图? [英] How Can Bind the Dragged view inside ScrollView item?

查看:59
本文介绍了如何在 ScrollView 项中绑定拖动视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用三个视图创建了 ScrollView.并且我想将拖动的视图绑定在 ScrollView 内,其余未拖放的视图应保持在 ScrollView 之外.怎么可能.

I have created ScrollView with three views. and I want to bind the dragged view inside the ScrollView, and the rest of haven't drag-dropped view should maintain outside of ScrollView. How can it be possible.

看 GIF:我想在 ScrollView 中添加 draggableView.

See the GIF : I want to add the draggableView inside the ScrollView.

代码:

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


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.dataDrag      =  [1,2,3,4],
    this.pan = this.dataDrag.map( () => new Animated.ValueXY() );

    this.state = {
        showDraggable   : true,
        dropZoneValues  : null,
        entries : ['Apple1' , 'Apple2', 'Apple3']
    };
}

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){
  console.log('event.nativeEvent.layout', event.nativeEvent.layout);
    this.setState({
        dropZoneValues : event.nativeEvent.layout
    });
}

_renderItem ({item, index}) {
    return (
        <View style={{width: Dimensions.get('window').width - 100 , height : 150 , backgroundColor : 'red' , marginLeft : 50 , marginRight : 50}}>
            <Text style={{color : 'black' , marginTop : 20}}>{item}</Text>
        </View>
    );
}

render(){
    return (
        <View style={styles.mainContainer}>

            <ScrollView>
              <View onLayout={this.setDropZoneValues.bind(this)}
                  style={[styles.dropZone , {marginTop : 10}]}>
              </View>
              <View onLayout={this.setDropZoneValues.bind(this)}
                  style={[styles.dropZone , {marginTop : 10}]}>
              </View>
              <View onLayout={this.setDropZoneValues.bind(this)}
                  style={[styles.dropZone , {marginTop : 10}]}>
              </View>
            </ScrollView>


            {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
    },
});

推荐答案

其基本思想是创建两个数组,通过从一个结构中删除并复制到另一个结构来模拟移动效果.

Base idea of this is to create two arrays, simulate move effect by removing from one structure and copy to another.

  1. 创建两个数组

  1. Create two array

this.state = {
    dataDrag: [...this.dataDrag],
    dataDragged: [],
};

  • OnPanResponderRelease,移动到新数组.

    onPanResponderRelease           : (e, gesture) => {
        if(this.isDropZone(gesture)){
            let idx = this.state.dataDrag.indexOf(index+1);
            this.setState({
                showDraggable : false,
                dataDrag: [ ...this.state.dataDrag.slice(0, idx), ...this.state.dataDrag.slice(idx+1, this.state.dataDrag.length-1) ],
                dataDragged: [...this.state.dataDragged, this.state.dataDrag[idx]],
            });
        }
    

  • 创建两套Animated.View,一套在ScollView里面,一套在外面.

  • Create two set of Animated.View, one inside of ScollView, one outside of it.

    最终代码:

    import React, { Component } from 'react';
    import {
      StyleSheet,
      View,
      Text,
      PanResponder,
      Animated,
      Easing,
      Dimensions,
      Platform,
      TouchableOpacity,
      ScrollView,
    } 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,
            entries : ['Apple1' , 'Apple2', 'Apple3'],
            dataDrag: [...this.dataDrag],
            dataDragged: [],
        };
    }
    
    getPanResponder(index) {
        return PanResponder.create({
            onStartShouldSetPanResponder: () => {
                return true;
            },
            onPanResponderMove              : Animated.event([null,{
                dx  : this.pan[index].x,
                dy  : this.pan[index].y
            }]),
            onPanResponderRelease           : (e, gesture) => {
                if(this.isDropZone(gesture)){
                    let idx = this.state.dataDrag.indexOf(index+1);
                    this.setState({
                        showDraggable : false,
                        dataDrag: [ ...this.state.dataDrag.slice(0, idx), ...this.state.dataDrag.slice(idx+1, this.state.dataDrag.length-1) ],
                        dataDragged: [...this.state.dataDragged, this.state.dataDrag[idx]],
                    });
                }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
        });
    }
    
    _renderItem ({item, index}) {
        return (
            <View style={{width: Dimensions.get('window').width - 100 , height : 150 , backgroundColor : 'red' , marginLeft : 50 , marginRight : 50}}>
                <Text style={{color : 'black' , marginTop : 20}}>{item}</Text>
            </View>
        );
    }
    
    render(){
        return (
            <View style={styles.mainContainer}>
    
                <ScrollView>
                  <View onLayout={this.setDropZoneValues.bind(this)}
                      style={[styles.dropZone , {marginTop : 10}]}>
                  </View>
                  <View onLayout={this.setDropZoneValues.bind(this)}
                      style={[styles.dropZone , {marginTop : 10}]}>
                  </View>
                  <View onLayout={this.setDropZoneValues.bind(this)}
                      style={[styles.dropZone , {marginTop : 10}]}>
                  </View>
    
                {this.state.dataDragged.map((d, index) => (
                    <Animated.View
                        key={d-1}
                        style={[styles.draggableContainer, this.pan[d-1].getLayout(), styles.circle]}>
                        <Text style={styles.text}>Drag {d-1}</Text>
                    </Animated.View>
                  ))}
    
                </ScrollView>
    
                {this.state.dataDrag.map((d, index) => (
                    <Animated.View
                        key={d-1}
                        {...this.getPanResponder(d-1).panHandlers}
                        style={[styles.draggableContainer, this.pan[d-1].getLayout(), styles.circle]}>
                        <Text style={styles.text}>Drag {d-1}</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
        },
    });
    

    结果:

    这篇关于如何在 ScrollView 项中绑定拖动视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆