但我想要做的是当用户长按时,用户应该能够继续相同的触摸/按下并拖动新显示的Animated.View.
如果您熟悉Google云端硬盘应用,则它具有类似的功能.当用户长按列表中的任何项目时,它会显示可拖动的项目.用户可以直接拖动项目.
我想如果我可以在它开始显示之后动态地将Responder更改为可拖动项目,那么这将有效.
问题是
react-native是否提供了动态更改响应者的方法?
到目前为止我尝试了什么
>我尝试更改onStartShouldSetPanResponderCapture,onMoveShouldSetPanResponderCapture,onMoveShouldSetPanResponder,onPanResponderTerminationRequest的逻辑,以便一旦可拖动项目开始显示容器视图不应捕获开始并移动并接受终止请求也将false返回到可拖动项目的终止请求并返回如果它应该捕获事件.
>一个适用于我的解决方法是在容器顶部显示可拖动项目,不透明度较低,并将其捕获为假.一旦用户长按它,我就会改变它的不透明度,使其清晰可见.使用此方法,用户可以继续触摸以拖动项目.但容器实际上是一个列表行.因此,我需要创建许多可拖动的,因为用户可以长按任意一行.
但我认为这不是一个好的解决方案,如果我能改变响应者,那就太好了.
解决方法
据我所知,不,你不能动态地改变视图的响应者.
像onStartShouldSetPanResponderCapture这样的方法不适用于您尝试拖动的子视图的原因是这些方法是在触摸开始时触发的,根据定义,在您描述的行为中实现onStartShouldSetPanResponderCapture的子视图不会触摸开始时还存在.
但是,没有理由为什么应该在子视图上实现泛响应器方法:
解决方案
从实现中退后一步,所需的实际功能是应用程序中的某些组件需要成为泛响应者.当平移响应者移动时,您将收到触摸事件.此时,您可以在子视图上设置NativeProps以反映平移手势中的更改.
因此,如果您想移动子视图,则无需将该子视图作为响应者.您可以简单地将父级作为响应者,然后从父级更新子级道具.
我在下面实现了一个示例应用程序,这里是一步一步解释发生了什么:
>您有一个呈现ListView的组件.那个ListView是你的泛响应者.列表视图中的每个单元格都有一个TouchableOpacity,可响应长按.
>当长按事件发生时(onLongPress道具被行触发),您将在顶部以浮动视图重新渲染父组件.此视图的绝对位置由父组件this._prevIoUsLeft和this._prevIoUsTop拥有的两个属性控制.
>现在,这个浮动子视图并不关心响应触摸.父母已经回应了.所有孩子都关心的是它的两个位置属性.因此,要移动浮动子组件,您需要做的就是使用ListView提供的_handlePanResponderMove函数中子视图组件的setNativeProps更新其顶部和左侧属性.
当您处理触摸时,您不需要移动的组件实际上是侦听触摸事件的组件.被移动的组件只需要通过监听触摸事件的任何内容来更新其位置属性.
以下是您在Google云端硬盘应用中描述的longPress / Pan手势的完整代码:
import React,{ PropTypes } from 'react'; import { AppRegistry,ListView,PanResponder,StyleSheet,Text,TouchableOpacity,View,} from 'react-native'; class LongPressDrag extends React.Component { constructor() { super(); this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder.bind(this),onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder.bind(this),onPanResponderMove: this._handlePanResponderMove.bind(this),onPanResponderRelease: this._handlePanResponderEnd.bind(this),onPanResponderTerminate: this._handlePanResponderEnd.bind(this),}); this._prevIoUsLeft = 0; this._prevIoUsTop = 0; this._floatingStyles = { style: { left: this._prevIoUsLeft,top: this._prevIoUsTop,position: 'absolute',height: 40,width: 100,backgroundColor: 'white',justifyContent: 'center',} }; const rows = Array(11).fill(11).map((a,i) => i); this.state = { dataSource: new ListView.DataSource({ rowHasChanged: (row1,row2) => row1 !== row2,}).cloneWithRows(rows),nativeEvent: undefined,//Pan Responder can screw with scrolling. See https://github.com/facebook/react-native/issues/1046 scrollEnabled: true,} } getDragElement() { if (!this.state.nativeEvent) { return null; } return ( <View style={[this._floatingStyles.style,{top: this._prevIoUsTop,left: this._prevIoUsLeft} ]} ref={(floating) => { this.floating = floating; }} > <Text style={{alignSelf: 'center'}}>Floating Item</Text> </View> ) } render() { return ( <View> <ListView dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} style={styles.container} scrollEnabled={this.state.scrollEnabled} {...this._panResponder.panHandlers} /> {this.getDragElement.bind(this)()} </View> ) } renderRow(num) { return ( <TouchableOpacity style={styles.cell} onLongPress={this.handleLongPress.bind(this)} onPressIn={this.handlePressIn.bind(this)} > <Text style={styles.title}>{`Row ${num}`}</Text> </TouchableOpacity> ); } handleLongPress(event) { console.log(event); this.setState({ nativeEvent: event.nativeEvent,scrollEnabled: false,}) } handlePressIn(event) { this._prevIoUsLeft = event.nativeEvent.pageX - 50; this._prevIoUsTop = event.nativeEvent.pageY - 20; } _updateNativeStyles() { this.floating && this.floating.setNativeProps({style: {left: this._prevIoUsLeft,top: this._prevIoUsTop}}); } _handleStartShouldSetPanResponder(e,gestureState) { return true; } _handleMoveShouldSetPanResponder(e,gestureState) { return true; } _handlePanResponderMove(event,gestureState) { this._prevIoUsLeft = event.nativeEvent.pageX - 50; this._prevIoUsTop = event.nativeEvent.pageY - 20; this._updateNativeStyles(); } _handlePanResponderEnd(e,gestureState) { this._prevIoUsLeft += gestureState.dx; this._prevIoUsTop += gestureState.dy; this.setState({ nativeEvent: undefined,scrollEnabled: true}) } } const styles = StyleSheet.create({ container: { flex: 1,marginTop: 20,},cell: { flex: 1,height: 60,backgroundColor: '#d3d3d3',borderWidth: 3,borderColor: 'white',title: { paddingLeft: 20,}); AppRegistry.registerComponent('LongPressDrag',() => LongPressDrag);
RN 0.29对我有用.我确信在这里可以做很多优化,但我只是想在一个快速的早晨对它进行黑客攻击来说明一般概念.
我希望这有帮助!