Egret社区

自动回收事件框架分享

2020-3-25 20:57
165124
本帖最后由 cair 于 2020-3-31 14:49 编辑

一行代码完成事件监听,自动释放。再也不需要写  egret.Event.REMOVED_FROM_STAGE  //     :)


// For example
UIUtils.onTap(this.button, () => { /** do something */ }, this);


完整代码:
namespace cair {

    /** 存储在回收池中的类型 */
    type IRecycleEvent = {
                /** 索引,是 thisObj 的 hashCode */
        hashCode: number;
        /** 事件集合,传参 thisObj 相同的都保存在这 */
        events: {
            func: Function;
            type: string;
            dispatcher: egret.EventDispatcher;
        }[];
                /** onTap  的 第三个参数,也就是函数执行时的 this */
        thisObj: any;
    }


    export class UIUtils {
   

                /** 回收池,索引是 hashCode */
        private static _autoRecyclePool: { [key: number]: IRecycleEvent } = {};

        /** 开启日志 */
        static _debug = false;


        private static log(message?: any, ...optionalParams: any[]) {
            if (this._debug) {
                console.info(message, optionalParams);
            }
        }

        /** 移除回收池中的事件 */
        private static removeEvent(hashCode: number) {
            return function () {
                UIUtils.log(`REMOVED_FROM_STAGE ${hashCode}`);
                let recylePool = UIUtils._autoRecyclePool[hashCode];
                if (recylePool == undefined) {
                    UIUtils.log(
                        "REMOVE_RECYLE_EVENT FAILED",
                        hashCode,
                        "removeEventArgs = undefined"
                    );
                    return;
                }
                while (recylePool.events.length > 0) {
                    let index = recylePool.events.length - 1;
                    UIUtils.releaseEvent(recylePool, index);
                }
                UIUtils.log(`REMOVE_RECYLE_EVENT_${hashCode}`);
                delete UIUtils._autoRecyclePool[hashCode];
                UIUtils.log("_autoRecyclePool", UIUtils._autoRecyclePool);
            };
        }

        /** thisObj 已经在回收池中,只需要把事件添加进去 */
        private static pushEvent(hashcode, events, thisObj) {
            let thisArg = UIUtils._autoRecyclePool[hashcode];
            // thisArg.events = thisArg.events.concat(events);
            ArrayUtils.concat(thisArg.events, events);
            UIUtils.log(
                `ADD_RECYLE_EVENT_${hashcode}__${events[0].type}`,
                egret.getQualifiedClassName(thisObj)
            );


            for (let evt of events) {
                evt.dispatcher.addEventListener(evt.type, evt.func, thisObj);
            }
        }

        /** 创建自动回收事件的对象, 实现机制看这个方法就明白了 */
        private static createEvents(thisHashCode, events, thisObj) {

            UIUtils.log(
                `PUSH_RECYLE_EVENT_${thisHashCode}__${events[0].type}`,
                egret.getQualifiedClassName(thisObj)
            );

            // 加入移除监听
            let _remove = function () {
                UIUtils.log("_remove _RECYLE_EVENT_", thisHashCode);
                thisObj.removeEventListener(
                    egret.Event.REMOVED_FROM_STAGE,
                    _remove,
                    thisObj
                );
                UIUtils.removeEvent(thisHashCode)();
            };


            thisObj.addEventListener(egret.Event.REMOVED_FROM_STAGE, _remove, thisObj);


            UIUtils._autoRecyclePool[thisHashCode] = {
                hashCode: thisHashCode,
                events: events,
                thisObj
            };


            for (let evt of events) {
                evt.dispatcher.addEventListener(evt.type, evt.func, thisObj);
            }
        }



        /**
        * 添加事件到自动回收池
        * @param events
        * @param obj
        * @param thisObj
        */
        private static addAutoRecyleEventPool(events: Array<{ func; type; dispatcher }>, obj: egret.EventDispatcher, thisObj) {
            if (thisObj == null) {
                return UIUtils.noThisObjAutoRecyle(events, obj, thisObj);
            }


            // 若该thisObj有值
            let thisHashCode = thisObj.hashCode;


            UIUtils._autoRecyclePool[thisHashCode] != null
                ? UIUtils.pushEvent(thisHashCode, events, thisObj)
                : UIUtils.createEvents(thisHashCode, events, thisObj);
        }


        /**
        * 没有指定删除对象
        */
        private static noThisObjAutoRecyle(events: Array<{ func; type }>, obj: egret.EventDispatcher, thisObj) {
            let remove = () => {
                while (events.length > 0) {
                    let evt = events.shift();
                    obj.removeEventListener(evt.type, evt.func, thisObj);
                }
            };


            events.push({ func: remove, type: egret.Event.REMOVED_FROM_STAGE });
            for (let i of events) obj.addEventListener(i.type, i.func, thisObj);
        }




        /**
        * @returns repeat item index
        * @param thisHashCode
        * @param eventType
        * @param dispatcher
        */
        private static checkEventRepeat(thisHashCode, eventType, dispatcher) {
            let pool = UIUtils._autoRecyclePool[thisHashCode];


            if (pool != null) {
                for (let i in pool.events) {
                    let evt = pool.events;
                    if (evt.type == eventType && evt.dispatcher.$hashCode == dispatcher.$hashCode) {
                        return +i;
                    }
                }
            }


            return null;
        }


        /**
        * 释放事件
        * @param pool
        * @param index
        */
        private static releaseEvent(pool: IRecycleEvent, index) {
            let event = pool.events[index];
            let thisObject = pool.thisObj;
            event.dispatcher.removeEventListener(event.type, event.func, thisObject);
            pool.events.splice(index, 1);
            UIUtils.log("Release Event ", event.dispatcher.$hashCode, event.type);
            event.dispatcher = null;
            event.func = null;
            event.type = null;
            event = null;
        }


        /**
        *
        * @param obj 需要监听事件的主体
        * @param func 监听方法
        * @param thisObj
        * @param eventType 事件类型
        * @param withSound 是否触发音效
        */
        static onTap(obj: egret.EventDispatcher, func: Function, thisObj: any, eventType: string = egret.TouchEvent.TOUCH_TAP, withSound = true) {


            // 检查是否已有相同监听
            let thisHashCode = thisObj.hashCode;


            let repeatIndex = UIUtils.checkEventRepeat(thisHashCode, eventType, obj);


            if (repeatIndex != null) {
                UIUtils.releaseEvent(UIUtils._autoRecyclePool[thisHashCode], repeatIndex);
            }

            // 可以扩展 禁止连点,等待任务执行完毕才可点击等
            function touchEvent(evt) {
                func.call(thisObj, evt);
                if (withSound) {
                    // play sound
                }
                UIUtils.log(egret.getQualifiedClassName(thisObj), "touchEvent call()");
            }
            UIUtils.addAutoRecyleEventPool(
                [{ func: touchEvent, type: eventType, dispatcher: obj }],
                obj,
                thisObj
            );
        }
    }
}



分享到 :
0 人收藏

24 个回复

倒序浏览
Hjx  略有小成 | 2020-3-26 10:05:31
看看
speall  初学乍练 | 2020-3-26 11:05:16
看看隐藏内容
heiye2046  登堂入室 | 2020-3-26 11:38:53
支持支持支持
app小王子  社区管理员 | 2020-3-26 12:07:07
棒!
另,小王子有个不情之请,希望可以直接看,不要隐藏内容。因为目前论坛因为一些政策原因,需要对大家的评论帖子审核之后才可以发出,所以这样子可能看起来有一点不方便。如果可以直接看的更好。
碎脸  登堂入室 | 2020-3-26 14:42:55
学习学习
temppublic  初学乍练 | 2020-3-26 17:18:56
kankan
李八疼  初窥堂奥 | 2020-3-26 17:46:31
看一看
cafebabe  登堂入室 | 2020-3-26 17:52:46
学习学习
m114  略有小成 | 2020-3-26 18:17:02
看看看看看
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|京网文[2014]0791-191号|京ICP证150115号|Egret社区 ( 京ICP备14025619号 )

Powered by Discuz! X3.4 © 2001-2019 Comsenz Inc.

返回顶部