PanResponder 内的 ScrollView

2023-12-25

我正在自己实现一个选项卡导航器,并在 React-Native 中滑动。它工作正常,但是当我的一个选项卡中有一个 ScrollView 时,它似乎会损坏。左右滑动来更改选项卡效果很好,并且在滚动视图中向下和向上滚动也可以。当我单击拖动滚动视图,然后向侧面移动而不释放滑动时,它会中断。然后选项卡系统将重置为第一个选项卡。

我做了一个黑客,当滚动视图滚动时,我禁止从选项卡内部滑动。这可行,但感觉像是一个糟糕的解决方案,因为选项卡内容必须知道它位于选项卡内。

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时水平滑动,如下所示:

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

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

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 上发现了 Satyajit Sahoo 写的一篇很棒的文章我如何构建 React Native 选项卡视图 https://medium.com/@satya164/how-i-built-react-native-tab-view-7adfb2570af6它更深入地展示了如何实现您自己的选项卡视图。我建议您查看该博客文章,因为它对我非常有帮助。

更新:查看此处的文档手势响应器生命周期 https://facebook.github.io/react-native/docs/gesture-responder-system.html#responder-lifecycle如果您希望父组件阻止子组件成为手势响应程序,反之亦然。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PanResponder 内的 ScrollView 的相关文章

随机推荐

  • 如何在java中生成XML(以字符串表示形式)

    我定义了一个java类 但只需要将该类的一些字段输出到XML中 返回的类型必须是 String 我首先选择了使用 StringBuffer 的最简单方法 但是 当我尝试处理输出字符串表示时 它失败了 我认为这很可能是因为输入中有些字符没有以
  • 在C中循环中重新声明数组

    当循环重复时 在循环中声明的数组不会设置为零吗 我有一个数组并以这种方式使用它 while i
  • C++0x 草案中的一点:n3290

    N3290 ISO 标准草案 3 4 1 12 中的一点 在查找中使用的名称期间常量表达式 of an 枚举数定义 之前声明的枚举器枚举的可见 并隐藏在包含该枚举的块 类或命名空间范围中声明的实体的名称枚举说明符 这是添加的新点 任何人都可
  • HTML5 视频:检测时间线的拖动

    我想知道是否有一种完全可靠的方法可以知道用户何时拖动 HTML5 视频的时间线 到目前为止 我一直绑定到 timeUpdate 但尚不清楚 currentTime 是否由于拖动而增加 或者只是由于视频播放而增加 谢谢 我知道MediaEle
  • H2数据库从1.4.200升级到2.0.204后集成测试出现问题

    最近我升级了H2数据库在我们的SpringBoot 2 5 8来自版本的项目1 4 200 to 2 0 204 它仅用于测试目的 对于生产 我们使用PostgreSQL 12 9 好像升级后有些词变成了关键词H2数据库例如 day val
  • 查询生成器/DQL 不适用于 INNER JOIN - 语法问题

    我知道我这里有语法问题 但我无法弄清楚 我正在尝试对 5 个表进行 SELECT 和 INNER JOIN 但 Symfony 抱怨 JOIN 中的实体在定义之前就被使用了 实际错误如下 Semantical Error line 0 co
  • symfony2 学说 expr 子查询:错误:参数号无效

    试图获得用户喜欢的状态 public function getLikedStatuses User user qb this gt em gt createQueryBuilder qb gt select s id gt from Wal
  • 匹配列表中至少两个单词的正则表达式

    我有一个单词列表 foo bar baz 我想编写一个正则表达式来匹配至少包含其中两个的字符串 例如 foo baz 应该匹配同时 ba foo z 不应该 显而易见的解决方案 foo bar baz foo bar baz 有效 但我觉得
  • JCL 错误 - “$HASP165 IBMUSERW ENDED AT SVSCJES2 - JCL ERROR CN(INTERNAL)”

    我正在尝试使用实用程序 DFHWS2LS 创建 CICS Web 服务请求程序 IBM 红皮书或指南中提到的第一步是在 JCL 中运行它 我在提交 JCL 时遇到了这个问题 我做错事了 无法弄清楚什么 任何有关此问题的帮助将不胜感激 IBM
  • 让 onClick 事件每次点击时执行不同的操作?

    我的表格中有 3 列 每列顶部都有一些文本 下面有一个图像 我拥有这样的功能 当有人单击 3 列之一中的图像时 它会放大该列 并使用 onClick 事件删除其他列 但是 我希望当我第二次单击图像时 它会带回其他列 我遇到的问题是弄清楚如何
  • 一对多 SVM 中的 10 倍交叉验证(使用 LibSVM)

    我想在我的系统中进行 10 倍交叉验证一对一 支持向量机 http en wikipedia org wiki Support vector machineMATLAB 中的分类 我试图以某种方式混合这两个相关的答案 libsvm 中的多类
  • 添加行时如何为 Angular ui-grid 设置动画

    我有一个 Angular UI 网格元素 我定期在数据数组的前面添加新项目 我一次只添加一些 例如 1 到 5 个新项目 我希望 UI 网格能够为添加的新行添加动画效果 现在 行会立即添加 这使得它变得紧张 我想要将新行添加到动画中 以便
  • 如何将两个向量相乘并得到一个矩阵?

    在numpy运算中 我有两个向量 假设向量A是4X1 向量B是1X5 如果我执行AXB 它应该产生一个大小为4X5的矩阵 但我尝试了很多次 进行了多种重塑和转置 它们要么引发错误说未对齐 要么返回单个值 我应该如何得到我想要的矩阵的输出乘积
  • 如何向 CouchDB (http://.couchone.com/) 执行 Ajax 请求

    我正在尝试创建一个简单的 AJAX 通过 jQuery 请求 http 您的用户名 couchone com 几乎与我在本地主机上安装了 couchdb 一样 如果我去http yourusername couchone com 通过浏览器
  • 如何在网站中实现搜索功能? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Delaunay 对带孔的二维多边形进行三角剖分

    我想对带有孔的复杂 但不是自相交 多边形进行三角剖分 以便生成的三角形全部位于多边形内部 完全覆盖该多边形 并遵守德劳内三角形规则 显然 我可以为所有点构建 Delaunay 三角剖分 但我担心多边形的某些边不会包含在生成的三角剖分中 那么
  • 比较两个字符串中的单词

    我做了两根弦 用户可以同时填写它们 char text 200 char text2 200 我需要从两个字符串中找到相似的单词 例如 Text 我一生都在这里 Text2 他们来这里是为了赢得我们所有人 我需要编程找到类似的单词 如 这里
  • 为 imageview 添加渐变

    我想在图像底部添加渐变 像这样的东西 我尝试了类似的方法 但我只得到渐变 没有图像
  • 混淆图像

    我想分发一些图像 并且不允许其他人看到它们 除非他们正在使用我的程序 我的目的是使用 JPG 文件 其中我将更改标题 以便其他图像查看器无法再阅读它们 例如 我可以删除字节 7 10 它们是 JPG 的魔术签名 稍后 我的程序将重建标题并显
  • PanResponder 内的 ScrollView

    我正在自己实现一个选项卡导航器 并在 React Native 中滑动 它工作正常 但是当我的一个选项卡中有一个 ScrollView 时 它似乎会损坏 左右滑动来更改选项卡效果很好 并且在滚动视图中向下和向上滚动也可以 当我单击拖动滚动视