PanResponder 中的 ScrollView [英] ScrollView inside an PanResponder

查看:33
本文介绍了PanResponder 中的 ScrollView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在通过在 React-Native 中滑动来实现我自己的选项卡导航器.它工作正常,但是当我的一个选项卡中有一个 ScrollView 时,它似乎坏了.向左和向右滑动以更改选项卡工作正常,并且还可以在滚动视图中向下和向上滚动.当我单击拖动 scrollView 然后横向移动而不释放滑动时它会中断.然后标签系统只是重置为第一个标签.

I am making my own implementation of a tab navigator with swiping in React-Native. It works fine, but when I have a ScrollView inside one of my tabs it seems to break. Swiping left and right to change tabs works fine, and also scrolling down and up in scrollview. It breaks when I click to drag the scrollView and then move sideways without releasing to swipe. Then the tab system just resets to the first tab.

我做了一个 hack,当滚动视图滚动时,我禁用了从选项卡内部滑动.这有效,但感觉像一个糟糕的解决方案,因为标签内容必须知道它在标签内.

I made a hack where I disable swiping from inside a tab when the scrollview is scrolled. This works, but feels like a bad solution because the tab content has to be aware that it is inside a tab.

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { View, Animated, Dimensions, PanResponder } from 'react-native';
import Immutable from 'immutable';
import Tab1 from './Tab1';
import Tab2 from './Tab2';
import ScrollViewTab from './ScrollViewTab';


@connect(
    state => ({
        tabs: state.tabs
    })
)

export default class Tabs extends Component {
    static propTypes = {
        tabs: PropTypes.instanceOf(Immutable.Map).isRequired,
        dispatch: PropTypes.func.isRequired
    };
    constructor(props) {
        super(props);
        this.justLoaded = true;
        this.state = {
            left: new Animated.Value(0),
            tabs: [{ // Tabs must be in order, despite index.
                name: 'tab1',
                component: <Tab1 setTab={this.setTab} />,
                index: 0
            }, {
                name: 'tab2',
                component: <Tab2 setTab={this.setTab} />,
                index: 1
            }, {
                name: 'scrollViewTab',
                component: <ScrollViewTab setTab={this.setTab} />,
                index: 2
            }]
        };
        this.getIndex = this.getIndex.bind(this);
    }
    componentWillMount() {
        this.panResponder = PanResponder.create({
            onMoveShouldSetResponderCapture: () => true,
            onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
                if (Math.abs(gestureState.dx) > 10) {
                    return true;
                }
                return false;
            },
            onPanResponderGrant: () => {
                this.state.left.setOffset(this.state.left._value);
                this.state.left.setValue(0);
            },
            onPanResponderMove: (e, gestureState) => {
                if (this.isSwipingOverLeftBorder(gestureState) ||
                    this.isSwipingOverRightBorder(gestureState)) return;
                Animated.event([null, {
                    dx: this.state.left
                }])(e, gestureState);
            },
            onPanResponderRelease: (e, gestureState) => {
                this.state.left.flattenOffset();

                if (this.isSwipingOverLeftBorder(gestureState) ||
                    this.isSwipingOverRightBorder(gestureState)) {
                    return;
                }

                Animated.timing(
                    this.state.left,
                    { toValue: this.calcX(gestureState) }
                ).start();
            }
        });
    }
    componentDidMount() {
        this.justLoaded = false;
    }
    getStyle() {
        const oldLeft = this.state.left;
        let left = 0;
        const screenWidth = Dimensions.get('window').width;
        // Set tab carouselle coordinate to match the selected tab.
        this.state.tabs.forEach((tab) => {
            if (tab.name === this.props.tabs.get('tab')) {
                left = -tab.index * screenWidth;
            }
        });


        if (this.justLoaded) {
            Animated.timing(
                this.state.left,
                { toValue: left,
                    duration: 0
                }
            ).start();

            return { transform: [{ translateX: oldLeft }], flexDirection: 'row', height: '100%' };
        }
        Animated.timing(
                this.state.left,
            { toValue: left
            }
            ).start();
        return { transform: [{ translateX: oldLeft }], flexDirection: 'row', height: '100%' };
    }
    getIndex(tabN) {
        let index = 0;

        this.state.tabs.forEach((tab) => {
            if (tab.name === tabN) {
                index = tab.index;
            }
            return tab;
        });

        return index;
    }
    setTab(tab, props) {
        this.navProps = props;
        this.props.dispatch({ type: 'SET_TAB', tab });
    }
    isSwipingOverLeftBorder(gestureState) {
        return (this.props.tabs.get('tab') === this.state.tabs[0].name &&
                gestureState.dx > 0);
    }
    isSwipingOverRightBorder(gestureState) {
        return (this.props.tabs.get('tab') === this.state.tabs[this.state.tabs.length - 1].name &&
                gestureState.dx < 0);
    }
    calcX(gestureState) {
        const screenWidth = Dimensions.get('window').width;
        const activeTab = this.getIndex(this.props.tabs.get('tab'));
        let coord = 0;

        if (gestureState.dx > screenWidth * 0.2) {
            coord = (activeTab * screenWidth) - screenWidth;
        } else if (gestureState.dx < -(screenWidth * 0.2)) {
            coord = (activeTab * screenWidth) + screenWidth;
        } else {
            coord = activeTab * screenWidth;
        }

        this.updateTab(-coord, screenWidth);

        return -coord;
    }
    updateTab(coord, screenWidth) {
        // Update current tab according to location and screenwidth
        this.state.tabs.forEach((tab) => {
            if (coord === -tab.index * screenWidth) {
                this.props.dispatch({ type: 'SET_TAB', tab: tab.name });
            }
        });
    }
    render() {
        return (
          <View
            style={{ flex: 1 }}
          >
            <Animated.View
              style={this.getStyle()}
              {...this.panResponder.panHandlers}
            >
              {this.state.tabs.map(tab => tab.component)}
            </Animated.View>
          </View>
        );
    }
}

推荐答案

尝试使用 onMoveShouldSetResponder 的函数,这样它只会在gestureState.dx 远大于gestureState.dy 时水平滑动,如下所示:

Try using a function for onMoveShouldSetResponder so it only swipes horizontally when gestureState.dx is far greater than gestureState.dy like so:

onMoveShouldSetResponder: (evt, gestureState) => {
    return Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3);
},

您还可以在 onPanResponderMove 中使用一个函数来跟踪滑动手势的方向,然后在 onPanResponderRelease 中重置,这样当垂直滑动变为水平滑动时就不会出现问题,如下所示:

You could also have a function in onPanResponderMove tracks the direction of the swipe gesture and then reset in onPanResponderRelease so you don't have problems when a vertical swipe changes into a horizontal swipe like so:

checkSwipeDirection(gestureState) {
    if( 
        (Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3) ) &&
        (Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 3) )
    ) {
        this._swipeDirection = "horizontal";
    } else {
        this._swipeDirection = "vertical";
    }
}
canMove() {
    if(this._swipeDirection === "horizontal") {
        return true;
    } else {
        return false;
    }
}

然后像这样使用它:

onMoveShouldSetPanResponder: this.canMove,
onPanResponderMove: (evt, gestureState) => {
    if(!this._swipeDirection) this.checkSwipeDirection(gestureState);

// Your other code here
},
onPanResponderRelease: (evt, gestureState) => {
    this._swipeDirection = null;
}

我在 Medium 我如何构建 React Native Tab View.它更深入地展示了如何实现您自己的 Tab View.我建议您看一下博文,因为它对我很有帮助.

I found an awesome article online by Satyajit Sahoo on Medium How I built React Native Tab View.It shows more in-depth how to implement your own Tab View. I recommend taking a look at the blog post as it was really helpful for me.

更新:在此处查看文档 Gesture响应者生命周期 如果您希望父组件阻止子组件成为手势响应者,反之亦然.

Update: Check out the documentation here Gesture Responder Lifecycle if you want a parent component to prevent a child component from becoming the gesture responder or vice versa.

这篇关于PanResponder 中的 ScrollView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆