ScrollView/PageView多级嵌套组件

ScrollView/PageView 多级嵌套滑动组件

开发环境

  • 引擎版本:Cocos Creator 2.4.0 / 3.6.0
  • 编程语言:TypeScript

已适配平台

H5 微信小游戏 Android原生 iOS原生 抖音小游戏 OPPO小游戏 vivo小游戏
:heavy_check_mark: :heavy_check_mark:

本资源仅支持测试通过平台,其他平台不做默认支持,请自行适配。(理论上支持全平台)

资源介绍

使用本组件,可以支持scrollview/pageview多层嵌套,多级滑动

scrollinscroll1.gif

功能特点

  • 支持多层嵌套,多层滑动
  • 使用ts编写,理论上与creator支持的平台一致
  • 需要注意,子Scroll组件不能开启回弹,所以在代码里做了判断,当有ScrollParent有父组件时,会关闭子Scroll组件的回弹

文档教程

  • 组件为源码发布,可自由调整和修改
  • 源码可以工程script/component内提取(ScrollViewExt.ts)
  • 只需要在ScrollView/PageView同级添加组件,并配置对应的父级即可
  • scrollinscrollp2.png

预览地址

预览

原理

介绍一下原理,如果有想自己实现的,也可以参照一下原理

  • ScrollViewExt组件,会替换(监管)ScrollView/PageView的touch事件
  • 在ScrollViewExt组件内,判断滑动方向,当滑动方向不是自己的滑动方向时,会向ScrollParent传递事件,直到与Scroll组件方向一致后停止传递
  • 判断子ScrollViewExt是否是滑动边界,如果是边界,也会向ScrollParent传递事件
  • 另外 PageView 的滚轮事件是空的,所以为了尽量少改动,所以 当有PageView时,会阻断滚轮事件

商店地址

store | scroll_in_scroll

1赞

image
如图结构:pageview横向滚动,3个全屏页面,shopPage,page1, page2
经典的皇室战争横向首页布局, shopPage 和 page2里面有纵向的 scrollview,使用直接把你插件里面的ScrollViewExt拖到 PageView,shopPage/ScrollView page2/ScrollView,子 scrollviewext 上面的挂 PageView 节点。程序运行后,页面在 shopPage 或 page2滑动在中心时,可以通过拖拽页面上面的 scrollview可以是 父 pageview 进行页面转换,以上这部分代码运转的很好。
但是,我的 page1上面没有任何 scrollview,那么当页面在转换到 page1在视图里之后,就无法转动 pageview 了,不明原因,似乎是 pageview 上由于挂载了scrollviewext 被影响了。
我改了你的代码:


    protected onLoad() {
        this._scrollView = this.node.getComponent(ScrollView);
        if (this._scrollView && this.ScrollParent) {
            this._scrollView.elastic = false;
        }

        this._button = this.node.getComponent(Button);

        if (this._scrollView != null) {

            this._onScrollViewTouchStart = this._scrollView["_onTouchBegan"].bind(this._scrollView);
            this._onScrollViewTouchMove = this._scrollView["_onTouchMoved"].bind(this._scrollView);
            this._onScrollViewTouchEnd = this._scrollView["_onTouchEnded"].bind(this._scrollView);
            this._onScrollViewTouchCancelled = this._scrollView["_onTouchCancelled"].bind(this._scrollView);
            this._onScrollViewMouseWheel = this._scrollView["_onMouseWheel"].bind(this._scrollView);

            this._scrollView["_onTouchBegan"] = function (event, captureListeners) { }
            this._scrollView["_onTouchMoved"] = function (event, captureListeners) { }
            this._scrollView["_onTouchEnded"] = function (event, captureListeners) { }
            this._scrollView["_onTouchCancelled"] = function (event, captureListeners) { }
            this._scrollView["_onMouseWheel"] = function (event, captureListeners) { }

        } else if (this._button != null) {

            this._onScrollViewTouchStart = this._button["_onTouchBegan"].bind(this._button);
            this._onScrollViewTouchMove = this._button["_onTouchMove"].bind(this._button);
            this._onScrollViewTouchEnd = this._button["_onTouchEnded"].bind(this._button);
            this._onScrollViewTouchCancelled = this._button["_onTouchCancel"].bind(this._button);

            this._button["_onTouchBegan"] = function (event) { }
            this._button["_onTouchMove"] = function (event) { }
            this._button["_onTouchEnded"] = function (event) { }
            this._button["_onTouchCancel"] = function (event) { }
        }

        this.node.on(Node.EventType.TOUCH_START, this._onTouchStart, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        this.node.on(Node.EventType.TOUCH_END, this._onTouchEnd, this);
        this.node.on(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
        this.node.on(Node.EventType.MOUSE_WHEEL, this._onMouseWheel, this);
    }

把没有 scrollview 的的子页面上面放置 Button,以此来代替 scrollview去向父 pageview 传递触摸信息。
下面还有一处touchmove,里面直接传递即可


    private _onTouchMove(event: EventTouch, captureListeners: Node[]) {
        if (!this.enabledInHierarchy) return;
        if (!this._isTouchStart) {
            return;
        }
        if (this.ScrollParent == null) {
            this._onScrollViewTouchMove(event, captureListeners);
            event.propagationStopped = true;
            return;
        }

        if (this._button != null) {
            this.ScrollParent.getComponent(ScrollViewExt)._onTouchMove(event, captureListeners);
            return;
        }
//...
}

还有一处 isBundary,不让空 scrollview报错即可
private _isBoundary(scrollView: ScrollView, boundary: ScrollBoundary): boolean {
if (scrollView == null) return true;
}
这样就伪造了一个 scrollview 的传递。
用起来一切正常了。
另外这个可以加在 button 上的修改过 的scrollviewext,也可在子页面的 scrollview 并非全屏,而在 scrollview覆盖以外的部分进行 pageview 的滑动。只需要在 pageview 的 page 节点处加 node,并放上这个 scrollviewext,当然也可以加载scrollview 前面的遮盖层上面。
当然了,如果不修改直接用也不是不能用,只不过我需要在 page1里面也放上一个 强制放上一个scrollview,虽然这个页面根本不需要上下的子层级滑动。

感谢反馈~

有个问题请教一下,我尝试复现一下,但没有出现不能滑动的问题,所以想问一下,你的page1中,都加了什么组件?有吞噬点击事件的组件么?

下面的是我尝试复现的结构
image
page_2里的button不是全屏的
page_2节点除了Sprite,没有加其他组件

另外,demo中,多层嵌套-2中page_3 是有没有scrollview的,可否在我的demo中复现一下呢?

1赞

这个嵌套组件,只需要在pageview/scrollview同级加上就可以,如果没有pageview/scrollview就可以不用加这个组件

价格 低廉 很好 赞一下

你的组件可以在KL大佬的List组件基础上生效嘛?

这个不确定,目前只适配了官方的组件

item不能button组件 有button组件就滑不动了

因为button会阻挡事件

官方的scrollview是可以滑动的

最近比较忙,等有时间了,我测试和修改一下

可以先自己实现个组件,挂在button节点上,这个组件可以设置一下ScrollViewExt组件的节点,也可以遍历父节点找到ScrollViewExt这个组件,并监听TOUCH_MOVE事件,如果有滑动就设置button取消点击事件,并调用ScrollViewExt的相关方法,试试能不能解决这个问题

滑动事件,可以参考一下

更新了一版,
增加ScrollViewExtButton组件,支持嵌套中,按钮的处理(只需要把ScrollViewExtButton组件挂在button节点为即可)

可以尝试一下

扩展成这样,就可以避免在按钮上拖拽后依然产生成功的点击。

// 在_onTouchStart中
        event.getLocationInView(this._touchStartInView);
// 在_onTouchEnd 中
        event.getLocationInView(this._touchStartInView2);
        if (Vec2.squaredDistance(this._touchStartInView, this._touchStartInView2) < 49) { // 
            this._onScrollViewTouchEnd(event, captureListeners);
        } else {
            this._onScrollViewTouchCancelled(event, captureListeners);
        }

现在一个问题是,当应用了该组件后,scrollview 的Elastic特性为什么要去掉呢,就是到尽头了可以继续阻尼滑动并回弹特性丢失了,想问下作者这个能补回来吗?

感谢反馈
另外,最新版本里增加了ScrollViewExtButton组件(如果需要其他组件可以参照一下),可以尝试一下

目前,最外层是可以有回弹的,子组件是关闭的,因为子组件在滑动时,会有边界等判断,如果外层有回弹,再加上子组件也有回弹,会导致边界等等判断错误,另外表现上也可能不是很好(子组件和外层同时回弹 或者 更多外层。。),实现上也比较复杂了,所以,暂时不打算在这个功能上扩展了
目前只能根据需求,自己扩展一下了。

demo还是有问题,快速上滑,pageview也会响应

pageview是左右滑动的,当向上滑动时,手指滑动并不是垂直的,会有左右偏移,由于速度快,所以pageview也可能会响应左右的滑动,可以通过调整pageview的参数去调整一下

ps: 即使不使用这个组件,pageview也会有这个问题。。