# ScrollViewNesting **Repository Path**: cocos2d-zp/scrollview-nesting ## Basic Information - **Project Name**: ScrollViewNesting - **Description**: ScrollView/PageView嵌套演示 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 27 - **Forks**: 19 - **Created**: 2021-05-10 - **Last Updated**: 2025-03-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README > ## ScrollView/PageView嵌套方案。 > 基于CocosCreator2.4.x ## 最近在群和论坛里发现不少ScrollView/PageView嵌套需求的小伙伴,但是Creator默认不支,我们尝试解决它。 # 一、分析原因 ___ 看一下源码就会发现每一个处理触摸(滚轮的就不管了)的函数最开始都有这两行 ``` _onTouchBegan(event, captureListeners) { if (!this.enabledInHierarchy) return; if (this._hasNestedViewGroup(event, captureListeners)) return; ... } ``` 不能嵌套的原因就在这个_hasNestedViewGroup函数里面 ``` ... if (event.target.getComponent(cc.ViewGroup)) { return true; } ... ``` # 二、解决问题 ___ 这个时候大部分小伙伴的做法可能都是继承ScrollView或PageView,然后重写一部分方法来解决嵌套的问题。 但是我们这里尝试换一个角度,比如我们手动发射一个假事件,让它不会被_hasNestedViewGroup过滤掉,并且我们的目标是让它成为一个单独的组件。 ___ 代码不多,直接上源码(新鲜出炉,未测BUG) demo项目地址:https://gitee.com/cocos2d-zp/scrollview-nesting ``` const { ccclass, property } = cc._decorator; interface EventTouch extends cc.Event.EventTouch { simulate?: boolean sham?: boolean } @ccclass export default class ViewGroupNesting extends cc.Component { private events: EventTouch[] = []; onLoad() { this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchHandle, this, true); this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchHandle, this, true); this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchHandle, this, true); this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchHandle, this, true); } private onTouchHandle(event: EventTouch) { if (event.sham || event.simulate || event.target === this.node) return; const cancelEvent: EventTouch = new cc.Event.EventTouch(event.getTouches(), event.bubbles); cancelEvent.type = event.type; cancelEvent.touch = event.touch; cancelEvent.sham = true; // 问:这里为啥不直接dispatchEvent // 答:必须让ScrollView把真的event先消耗掉,我们再发射假的才可以, // 可以去CCNode.js下找一个_doDispatchEvent函数,里面用到了_cachedArray这么个全局变量, // 先发射假的话,真的那个数据就被清空了 this.events.push(cancelEvent); } update() { if (this.events.length === 0) return; for (let index = 0; index < this.events.length; index++) { this.node.dispatchEvent(this.events[index]); } this.events.length = 0; } } ```