问题
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:
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() );
Your
PanResponder
need information from current index. Pull it out as a function that returns function including info ofindex
.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(); } } }); }
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> ))}
Do margin trick to reduce calculation of position. Change
top / left
tomarginTop / 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