react-native更改动态响应者

Abh*_*hay 8 android draggable uiresponder react-native

react-native用于Android开发.我有一个视图,如果用户长按,我想显示一个可以拖动的动画视图.我可以实现这个PanResponder,它工作得很好.

但我想要做的是当用户长按时,用户应该能够继续相同的触摸/按下并拖动新显示的Animated.View.

如果您熟悉Google云端硬盘应用,则它具有类似的功能.当用户长按列表中的任何项目时,它会显示可拖动项目.用户可以直接拖动项目.

在此输入图像描述

我想如果我可以Responder在它开始显示后动态更改为可拖动项目,那么这将有效.

问题是

react-native是否提供了动态更改响应者的方法?

到目前为止我尝试过的

  • 我试图改变的逻辑onStartShouldSetPanResponderCapture,onMoveShouldSetPanResponderCapture,onMoveShouldSetPanResponder,onPanResponderTerminationRequest,这样只要拖动项目启动表示箱体视图应该没有捕捉到开始和移动并接受终止请求也返回false来拖动项目的终止请求,并返回true,以它应该捕获事件.

  • 一个对我有用的解决方法是在容器顶部显示可拖动的项目,不透明度较低,并将其捕获为假.一旦用户长按它,我就会改变它的不透明度,使其清晰可见.通过这种方法,用户可以继续触摸以拖动项目.但容器实际上是一个列表行.因此,我需要创建许多可拖动的,因为用户可以长按任意一行.

但我认为这不是一个好的解决方案,如果我能改变响应者,那就太好了.

Mic*_*vey 6

简单的答案

据我所知,,你不能动态地改变视图的响应者.

类似的方法onStartShouldSetPanResponderCapture不适用于您尝试拖动的子视图的原因是这些方法是在触摸开始时触发的,并且根据定义,在onStartShouldSetPanResponderCapture您描述的行为中实现的子视图尚不存在触摸开始时

但是,没有理由为什么应该在子视图上实现泛响应方法:

解决方案

从实现中退一步,所需的实际功能是应用程序中的某些组件需要是泛响应程序.当平移响应者移动时,您将收到触摸事件.此时,您可以setNativeProps在子视图上反映平移手势的更改.

因此,如果您想移动子视图,则无需将该子视图作为响应者.您可以简单地将父级作为响应者,然后从父级更新子级道具.

我在下面实现了一个示例应用程序,这里是对正在发生的事情的一步一步解释:

  1. 你有一个组件呈现一个ListView.那ListView是你的泛响应者.列表视图中的每个单元格都TouchableOpacity响应长按.

  2. 当长按事件发生时(onLongPress由行触发道具),您将在顶部使用浮动视图重新渲染父组件.这种观点的绝对位置是由你的父组件拥有两个属性控制,this._previousLeftthis._previousTop.

  3. 现在,这个浮动子视图并不关心响应触摸.父母已经回复了.所有孩子都关心的是它的两个位置属性.于是四处移动浮动子组件,所有你所要做的就是更新其topleft使用性能setNativeProps的孩子的View分量,在_handlePanResponderMove通过所提供的功能ListView.

摘要

当你处理触摸,你没有需要的部件被移到实际上是一个监听触摸事件.被移动的组件只需要通过监听触摸事件的任何内容更新其位置属性.

以下是您在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',
    justifyContent: 'center',
  },
  title: {
    paddingLeft: 20,
  },
});

AppRegistry.registerComponent('LongPressDrag', () => LongPressDrag);
Run Code Online (Sandbox Code Playgroud)

RN 0.29对我有用.我确信这里可以进行大量的优化,但我只是想在一个快速的早晨对它进行黑客攻击来说明一般概念.

我希望这有帮助!