2 Star 11 Fork 8

dony / 学习笔记

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
让cocoscreator的ScrollView:PageView支持嵌套滑动.md 4.35 KB
一键复制 编辑 原始数据 按行查看 历史
dony 提交于 2021-09-10 02:09 . 让creator支持嵌套滑动

让cocoscreator的ScrollView/PageView支持嵌套滑动

最近为了实现皇室战争主页中嵌套滑动,在PageView中嵌入ScrollView,发现在ScrollView区域内拖拽UI,无法滑动PageView。原来是creator默认不支持嵌套滑动,这里尝试解决它。

  • 分析问题

先来看下源码就会发现每一个处理触摸的函数最开始都有这两行代码

// touch event handler
_onTouchBegan (event, captureListeners) {
  if (!this.enabledInHierarchy) return;
  if (this._hasNestedViewGroup(event, captureListeners)) return;

  ...
},
_onTouchMoved (event, captureListeners) {
  if (!this.enabledInHierarchy) return;
  if (this._hasNestedViewGroup(event, captureListeners)) return;

  ...
},
_onTouchEnded (event, captureListeners) {
  if (!this.enabledInHierarchy) return;
  if (this._hasNestedViewGroup(event, captureListeners)) return;

  ...
},
_onTouchCancelled (event, captureListeners) {
  if (!this.enabledInHierarchy) return;
  if (this._hasNestedViewGroup(event, captureListeners)) return;

  ...
},

分析代码后,就可以知道不能嵌套的原因就在这个_hasNestedViewGroup函数里面

//this is for nested scrollview
    _hasNestedViewGroup (event, captureListeners) {
        if (event.eventPhase !== cc.Event.CAPTURING_PHASE) return;

        if (captureListeners) {
            //captureListeners are arranged from child to parent
            for (let i = 0; i < captureListeners.length; ++i){
                let item = captureListeners[i];

                if (this.node === item) {
                    if (event.target.getComponent(cc.ViewGroup)) {
                        return true;
                    }
                    return false;
                }

                if(item.getComponent(cc.ViewGroup)) {
                    return true;
                }
            }
        }
        return false;
    },

原因就出在

if (event.target.getComponent(cc.ViewGroup)) {
    return true;
}

如果event.target的组件中不包含cc.ViewGroup,就可以解决这个问题。

  • 解决问题

很多小伙伴的做法很可能都是继承ScrollView/PageView,重写一个ScrollView/PageView来解决嵌套滑动问题。

这里借鉴论坛中“向前”大大的方法,不用这种继承ScrollView/PageView的侵入式方案,可以手动发射一个假事件,让它不会被_hasNestedViewGroup过滤掉,并且我们的目标是让它成为一个单独的组件

代码不多,直接上原吗

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;
    }
}
  • 使用方法

    直接拖到父ScrollView/PageView上即可,具体入下效果:

如此,就实现了PageView/ScrollView的嵌套滑动,具体效果就不贴出图了,有兴趣的小伙伴可以自己去试下。

1
https://gitee.com/dony1122/note.git
git@gitee.com:dony1122/note.git
dony1122
note
学习笔记
master

搜索帮助