【问题标题】:react-native change responder dynamically动态响应本机变更响应程序
【发布时间】:2016-11-07 00:15:27
【问题描述】:

我正在使用 react-native 进行 Android 开发。我有一个视图,如果用户长按,我想显示一个可以拖动的动画视图。我可以使用PanResponder 来实现这一点,效果很好。

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

如果您熟悉 Google Drive 应用程序,它具有类似的功能。当用户长按列表中的任何项目时,它会显示可拖动项目。用户可以直接拖动项目。

我想如果我可以在 Responder 开始显示后将其动态更改为可拖动项目,那么这将起作用。

问题是

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

到目前为止我已经尝试过什么

  • 我尝试更改onStartShouldSetPanResponderCaptureonMoveShouldSetPanResponderCaptureonMoveShouldSetPanResponderonPanResponderTerminationRequest 的逻辑,以便在可拖动项目开始显示容器视图时不应该捕获开始并移动并接受终止请求还向可拖动项目的终止请求返回 false 并对其返回 true 应该捕获事件。

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

但我认为这不是一个好的解决方案,如果我可以更改响应者,那就太好了。

【问题讨论】:

标签: android react-native draggable uiresponder


【解决方案1】:

简单回答

据我所知,,您无法动态更改视图的响应者。

onStartShouldSetPanResponderCapture 之类的方法在您尝试拖动的子视图上不起作用的原因是,这些方法在触摸时触发start,根据定义,子视图触摸开始时,在您描述的行为中实现onStartShouldSetPanResponderCapture 的视图尚不存在。

但是,没有理由应该在子视图上实现 pan 响应器方法:

解决方案

从实现中退一步,实际所需的功能是应用程序中的 some 组件需要是一个平移响应器。当平移响应器移动时,您会收到触摸事件。此时,您可以在子视图上setNativeProps 以反映平移手势的变化。

因此,如果您想移动子视图,则无需实际使该子视图成为响应者。您可以简单地使 parent 成为响应者,然后从 parent 更新子 props。

我已经在下面实现了一个示例应用程序,下面是对正在发生的事情的分步说明:

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

  2. 当长按事件发生时(onLongPress 属性被行触发),你重新渲染你的父组件,顶部有一个浮动视图。此视图的绝对位置由您的父组件拥有的两个属性控制,this._previousLeftthis._previousTop

  3. 现在,这个浮动子视图并不关心对触摸的响应。家长已经在回复了。所有孩子关心的是它的两个位置属性。因此,要移动浮动子组件,您所要做的就是在ListView 提供的_handlePanResponderMove 函数中使用子View 组件的setNativeProps 更新其topleft 属性.

总结

当您处理触摸时,您不需要将要移动的组件真正成为监听触摸事件的组件。被移动的组件只需要通过监听触摸事件的任何来更新其位置属性。

这是您在 Google Drive 应用中描述的长按/平移手势的完整代码:

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);

这对我来说适用于 RN 0.29。我确信这里可以进行很多优化,但我只是想在一个快速破解的早晨说明一般概念。

我希望这会有所帮助!

【讨论】:

  • 我刚刚在 Android (RN 0.29) 上尝试了你的代码 sn-p 并且 longPress 不起作用。
  • 我刚刚在 Android(Nexus 5X 模拟器)上运行它,它运行良好。 onLongPress 被解雇花了 真的 很长时间(与 iOS 相比)。因此,也许您需要使用delayLongPress 道具或其他东西。但是代码 sn-p 对我来说在两个平台上都可以正常工作。
  • 好的,感谢您测试@Michael Helvey。不幸的是,它在我的物理设备甚至模拟器上仍然对我不起作用,但我怀疑这与这个特定的 sn-p 无关。
  • (你使用的是 RN 0.29 吗?)
  • 是的。我正在使用 RN 0.29。如果我不能更好地测试它,我很抱歉——我没有很多原生 Android 经验(我来自原生 iOS 背景)。但是,如果我从我的 Android 工作室项目中运行它,它似乎可以正常工作。根据 RN 文档,理论上,onLongPress 道具的行为不应该是特定于平台的。
猜你喜欢
  • 2021-05-21
  • 2019-02-22
  • 2021-03-12
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-10
  • 2021-03-31
相关资源
最近更新 更多