简体   繁体   English

React-native:panResponder 内的滚动视图

[英]React-native: scrollview inside of panResponder

I am using a ScrollView inside of a PanResponder.我在 PanResponder 中使用了 ScrollView。 On Android it works fine but on iOS the ScrollView will not scroll.在 Android 上它工作正常,但在 iOS 上 ScrollView 不会滚动。 I did some investigation and here are some facts:我做了一些调查,这里有一些事实:

  1. If I put a break point in PanResponder.onMoveShouldSetPanResponder() , before I step over, the scrollView will scroll as normal but once I release the break point, the scrollView stops working.如果我在PanResponder.onMoveShouldSetPanResponder()放置一个断点,在我跨过去之前,scrollView 将正常滚动,但是一旦我释放断点,scrollView 就会停止工作。

  2. If I modify ScrollResponder.js, and return true in scrollResponderHandleStartShouldSetResponderCapture() - it used to return false at runtime;如果我修改 ScrollResponder.js,并在scrollResponderHandleStartShouldSetResponderCapture()返回 true - 它曾经在运行时返回 false; and return false in scrollResponderHandleTerminationRequest() , the scrollView works OK but of course, since it swallows the event the outer PanResponder will not get the event.并在scrollResponderHandleTerminationRequest()返回 false ,scrollView 工作正常,但当然,因为它吞下了事件,外部PanResponder将不会获得该事件。

So the questions are:所以问题是:

  1. I want to make the scrollview to work, and not to swallow the event.我想让滚动视图工作,而不是吞下事件。 Any one know what's the approach?有人知道是什么方法吗?
  2. How the responding system works on iOS?响应系统如何在 iOS 上工作? The react-native responder system doc does not explain that to me. react-native 响应系统文档没有向我解释这一点。

I finally solve this by wrap the scrollview inside a view ,and set the style of scrollview a limited height.我最终通过将滚动视图包裹在视图中来解决这个问题,并将滚动视图的样式设置为有限的高度。

import * as React from 'react';
import { Text, View, StyleSheet,PanResponder,Animated,ScrollView,Dimensions} from 'react-native';
import { Constants } from 'expo';

const WINDOW_WIDTH = Dimensions.get("window").width;
const WINDOW_HEIGHT = Dimensions.get("window").height;
// You can import from local files

export default class App extends React.Component {
  constructor(props){
    super(props);
    this.minTop = 100;
    this.maxTop = 500;
    this.state={
      AnimatedTop:new Animated.Value(0),
    }
  }

  componentWillMount(){
    let that = this;
    this._previousTop = 100;
    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponder(){
        return true;
      },
      onPanResponderGrant(){
        that._previousTop = that.state.AnimatedTop.__getValue();
        return true;
      },
      onPanResponderMove(evt,gestureState){
        let currentTop = that._previousTop + gestureState.dy;
        that.state.AnimatedTop.setValue(that._previousTop+gestureState.dy);

      },
      onPanResponderRelease(){

      }
    })
  }


  render() {
    return (
      <View style={styles.container}>
        <Animated.View
          style={[styles.overlay,{top:this.state.AnimatedTop}]}
          {...this._panResponder.panHandlers}
        >
          <View style={{height:200,backgroundColor:"black"}}></View>
          <View>
            <ScrollView
              style={{height:500}}
            >
              <View style={{backgroundColor:"blue",height:200}}></View>
              <View style={{backgroundColor:"yellow",height:200}}></View>
              <View style={{backgroundColor:"pink",height:200}}></View>
              <View style={{backgroundColor:"red",height:200}}></View>
            </ScrollView>
          </View>
        </Animated.View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  overlay:{
    position:"absolute",
    width:WINDOW_WIDTH,
    height:WINDOW_HEIGHT-100,
  }
});

enter link description here I spent plenty of time to solve this.Hope this will help someone confused with the same problem. 在这里输入链接描述我花了很多时间来解决这个问题。希望这会帮助那些对同样问题感到困惑的人。

Within PanResponder is an event that returns the current touch position. PanResponder 中有一个返回当前触摸位置的事件。 You can use that to compare 2 values to perform a scroll.您可以使用它来比较 2 个值以执行滚动。

I don't know if it's usefull right now but you can add that line,我不知道它现在是否有用,但您可以添加该行,

return !(gestureState.dx === 0 && gestureState.dy === 0)

in 'onMoveShouldSetPanResponder' property of PanResponder with evt and gestureState as parameters.在 PanResponder 的 'onMoveShouldSetPanResponder' 属性中,以 evt 和gestureState 作为参数。

Your PanResponder should look like this :您的 PanResponder 应如下所示:

this._panResponder = PanResponder.create({
  onMoveShouldSetPanResponder(evt, gestureState){
    return !(gestureState.dx === 0 && gestureState.dy === 0)
  },
  onPanResponderGrant(){
    that._previousTop = that.state.AnimatedTop.__getValue();
    return true;
  },
  onPanResponderMove(evt,gestureState){
    let currentTop = that._previousTop + gestureState.dy;
    that.state.AnimatedTop.setValue(that._previousTop+gestureState.dy);

  },
  onPanResponderRelease(){

  }
})

我已经通过向 ScrollView 的所有内容添加onPress处理程序解决了这个问题 - 如果处理程序是无操作的,滚动视图仍然可以正常工作。

I solved this issue by doing it我通过这样做解决了这个问题

onMoveShouldSetPanResponder: (event, gesture) => {
  if (gesture?.moveX > gesture?.moveY) {
    return false;
  }
  return true;
},

On my case, I have a horizontal Flatlist inside a PanResponder...就我而言,我在 PanResponder 中有一个水平 Flatlist ......

To enable scrolling in a ScrollView that is a child of a parent with PanResponder, you have to make sure the ScrollView is the responder to any gesture inside of it.要在具有 PanResponder 的父级的子级 ScrollView 中启用滚动,您必须确保 ScrollView 是其中任何手势的响应者。 By default, gesture events bubble up from the deepest component to the parent component.默认情况下,手势事件从最深的组件冒泡到父组件。 In order to capture the event by the ScrollView, you can add a View with a PanResponder inside of it.为了通过 ScrollView 捕获事件,您可以添加一个内部带有 PanResponder 的视图。 See the (pseudo) example below, where ChildComponent is a child of a parent with PanResponder.请参阅下面的(伪)示例,其中ChildComponent是具有 PanResponder 的父级的子级。

const ChildComponent = ({ theme }) => {
    const panResponder = React.useRef(
        PanResponder.create({
            // Ask to be the responder:
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
        })
    ).current;

    return (
        <ScrollView style={{ height: 500 }}>
            <View {...panResponder.panHandlers}>
                ...
            </View>
        </ScrollView>
    );
};

I'm not sure you still need this or not but I'll put to help others as well我不确定你是否还需要这个,但我也会帮助别人

If you put the panResponder on a sibling view of the ScrollView, the ScrollView behaves properly.如果将 panResponder 放在 ScrollView 的同级视图上,则 ScrollView 的行为正常。 then you can position that sibling view然后您可以定位该兄弟视图

here is an example of the workaround.这是解决方法的示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM