import UIView , { UIClass } from "../ui/UIView" ; import { ResourceInfo , ResourceCacheData , ViewStatus , BUNDLE_TYPE , BUNDLE_RESOURCES } from "./Defines" ; import { Manager } from "../Framework" ; description const DYNAMIC_LOAD_GARBAGE = "DYNAMIC_LOAD_GARBAGE" ; description const DYNAMIC_LOAD_RETAIN_MEMORY = "DYNAMIC_LOAD_RETAIN_MEMORY" ; class ViewDynamicLoadData { private local = new Map < string , ResourceInfo > ( ) ; private remote = new Map < string , ResourceInfo > ( ) ; public name : string ; constructor ( name = null ) { this . name = name ; } description public addLocal ( info , className = null ) { if ( info && info . url ) { if ( this . name == DYNAMIC_LOAD_GARBAGE ) { cc . error ( `找不到资源持有者: ${ info . url }` ) ; } if ( CC_DEBUG ) Manager . uiManager . checkView ( info . url , className ) ; if ( ! this . local . has ( info . url ) ) { this . local . set ( info . url , info ) ; } } } description public addRemote ( info , className = null ) { if ( info && info . data && ! this . remote . has ( info . url ) ) { if ( this . name == DYNAMIC_LOAD_GARBAGE ) { cc . error ( `找不到资源持有者 : ${ info . url }` ) ; } if ( CC_DEBUG ) Manager . uiManager . checkView ( info . url , className ) ; Manager . cacheManager . remoteCaches . retainAsset ( info ) ; this . remote . set ( info . url , info ) ; } } description public clear ( ) { if ( this . name == DYNAMIC_LOAD_GARBAGE ) { let isShow = this . local . size > 0 || this . remote . size > 0 ; if ( isShow ) { cc . error ( `当前未能释放资源如下:` ) ; } if ( this . local && this . local . size > 0 ) { cc . error ( "-----------local-----------" ) ; if ( this . local ) { this . local . forEach ( ( info ) => { cc . error ( info . url ) ; } ) ; } } if ( this . remote && this . remote . size > 0 ) { cc . error ( "-----------remote-----------" ) ; if ( this . remote ) { this . remote . forEach ( ( info , url ) => { cc . error ( info . url ) ; } ) ; } } } else { if ( this . local ) { this . local . forEach ( ( info ) => { Manager . assetManager . releaseAsset ( info ) ; } ) ; this . local . clear ( ) ; } if ( this . remote ) { this . remote . forEach ( ( info , url ) => { Manager . cacheManager . remoteCaches . releaseAsset ( info ) ; } ) ; this . remote . clear ( ) ; } } } } description class ViewData { description isLoaded : boolean = false ; description status : ViewStatus = ViewStatus . WAITTING_NONE ; description view : UIView = null ; description finishCb : ( ( view : any ) => void ) [ ] = [ ] ; description getViewCb : ( ( view : any ) => void ) [ ] = [ ] ; /**是否预加载,不显示出来，但会加到当前场景上 */ isPreload : boolean = false ; description info : ResourceInfo = null ; description loadData : ViewDynamicLoadData = new ViewDynamicLoadData ( ) ; node : cc . Node = null ; private doGet ( view , className , msg ) { for ( let i = 0 ; i < this . getViewCb . length ; i ++ ) { let cb = this . getViewCb [ i ] ; if ( cb ) { cb ( view ) ; if ( CC_DEBUG ) cc . warn ( `ViewData do get view : ${ className } msg : ${ msg }` ) ; } } this . getViewCb = [ ] ; } private doFinish ( view , className , msg ) { for ( let i = 0 ; i < this . finishCb . length ; i ++ ) { let cb = this . finishCb [ i ] ; if ( cb ) { cb ( view ) ; if ( CC_DEBUG ) cc . warn ( `ViewData do finish view : ${ className } msg : ${ msg }` ) ; } } this . finishCb = [ ] ; } doCallback ( view , className , msg ) { this . doFinish ( view , className , msg ) ; this . doGet ( view , className , msg ) ; } } export class UIManager { private static _instance : UIManager = null ; public static Instance ( ) { return this . _instance || ( this . _instance = new UIManager ( ) ) ; } public _logTag = `[UIManager]` ; description private _viewDatas : Map < string , ViewData > = new Map < string , ViewData > ( ) ; private getViewData ( className ) ; private getViewData < T extends UIView > ( uiClass ) ; private getViewData ( data ) { let className = this . getClassName ( data ) ; if ( ! className ) return null ; let viewData = this . _viewDatas . has ( className ) ? this . _viewDatas . get ( className ) : null ; return viewData ; } private getClassName ( className ) ; private getClassName < T extends UIView > ( uiClass ) ; private getClassName ( data ) { if ( ! data ) return null ; let className = null ; if ( typeof data == "string" ) { className = data ; } else { className = cc . js . getClassName ( data ) ; } return className ; } description private GC_INTERVAL = 1500 ; description private _lastGCTime = 0 ; description private _isNeedGC = false ; description public garbage = new ViewDynamicLoadData ( DYNAMIC_LOAD_GARBAGE ) ; description public retainMemory = new ViewDynamicLoadData ( DYNAMIC_LOAD_RETAIN_MEMORY ) ; public preload < T extends UIView > ( uiClass , bundle ) { return this . _open ( uiClass , bundle , 0 , true , null , null ) ; } description param config param config . type param config . zIndex param config . args param config . delay param config . name example description param uiClass param zIndex param args public open < T extends UIView > ( config ) { return this . _open ( config . type , config . bundle , config . zIndex ? config . zIndex : 0 , false , config . args , config . delay , config . name ) ; } private _open < T extends UIView > ( uiClass , bundle , zOrder = 0 , isPreload , args , delay , name ? ) { return new Promise < T > ( ( reslove , reject ) => { if ( ! uiClass ) { if ( CC_DEBUG ) cc . log ( `${ this . _logTag }open ui class error` ) ; reslove ( null ) ; return ; } let className = cc . js . getClassName ( uiClass ) ; let canvas = this . getCanvas ( ) ; if ( ! canvas ) { if ( CC_DEBUG ) cc . error ( `${ this . _logTag }找不到场景的Canvas节点` ) ; reslove ( null ) ; return ; } this . recordGCTime ( ) ; let viewData = this . getViewData ( uiClass ) ; if ( viewData ) { viewData . isPreload = isPreload ; if ( viewData . isLoaded ) { viewData . status = ViewStatus . WAITTING_NONE ; if ( ! isPreload ) { if ( viewData . view && cc . isValid ( viewData . node ) ) { viewData . node . zIndex = zOrder ; if ( ! viewData . node . parent ) { viewData . node . parent = this . getCanvas ( ) ; } Manager . resolutionHelper . fullScreenAdapt ( viewData . node ) ; viewData . view . show ( args ) ; } } reslove ( < T > viewData . view ) ; return ; } else { viewData . status = ViewStatus . WAITTING_NONE ; if ( ! isPreload ) { Manager . uiLoading . show ( delay , name ) ; } if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }${ className } 正在加载中...` ) ; viewData . finishCb . push ( reslove ) ; return ; } } else { viewData = new ViewData ( ) ; viewData . loadData . name = className ; let prefabUrl = uiClass . getPrefabUrl ( ) ; viewData . isPreload = isPreload ; this . _viewDatas . set ( className , viewData ) ; let progressCallback = null ; if ( ! isPreload ) { Manager . uiLoading . show ( delay , name ) ; progressCallback = ( completedCount , totalCount , item ) => { let progress = Math . ceil ( ( completedCount / totalCount ) * 100 ) ; Manager . uiLoading . updateProgress ( progress ) ; } ; } this . loadPrefab ( bundle , prefabUrl , progressCallback ) . then ( ( prefab ) => { viewData . info = new ResourceInfo ; viewData . info . url = prefabUrl ; viewData . info . type = cc . Prefab ; viewData . info . data = prefab ; viewData . info . bundle = bundle ; this . createNode ( className , uiClass , reslove , prefab , args , zOrder , bundle ) ; Manager . uiLoading . hide ( ) ; } ) . catch ( ( reason ) => { viewData . isLoaded = true ; cc . error ( reason ) ; this . close ( uiClass ) ; viewData . doCallback ( null , className , "打开界面异常" ) ; reslove ( null ) ; let uiName = "" ; if ( CC_DEBUG ) { uiName = className ; } if ( name ) { uiName = name ; } Manager . tips . show ( `加载界面${ uiName }失败，请重试` ) ; Manager . uiLoading . hide ( ) ; } ) ; } } ) ; } private _addComponent < T extends UIView > ( uiNode , uiClass , viewData , className , zOrder , args , bundle ) { if ( uiNode ) { let view = uiNode . getComponent ( uiClass ) as UIView ; if ( ! view ) { view = uiNode . addComponent ( uiClass ) ; if ( ! view ) { if ( CC_DEBUG ) cc . error ( `${ this . _logTag }挂载脚本失败 : ${ className }` ) ; return null ; } else { if ( CC_DEBUG ) cc . log ( `${ this . _logTag }挂载脚本 : ${ className }` ) ; } } Manager . resolutionHelper . fullScreenAdapt ( uiNode ) ; view . className = className ; view . bundle = bundle ; viewData . view = view ; ( < any > view ) . _args = args ; let widget = view . getComponent ( cc . Widget ) ; if ( widget ) { if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }你已经添加了cc.Widget组件，将会更改成居中模块` ) ; widget . isAlignHorizontalCenter = true ; widget . horizontalCenter = 0 ; widget . isAlignVerticalCenter = true ; widget . verticalCenter = 0 ; } else { widget = view . addComponent ( cc . Widget ) ; widget . isAlignHorizontalCenter = true ; widget . horizontalCenter = 0 ; widget . isAlignVerticalCenter = true ; widget . verticalCenter = 0 ; } if ( ! viewData . isPreload ) { uiNode . parent = this . getCanvas ( ) ; uiNode . zIndex = zOrder ; } return view ; } else { return null ; } } private createNode < T extends UIView > ( className , uiClass , reslove , data , args , zOrder , bundle ) { let viewData = this . _viewDatas . get ( className ) ; viewData . isLoaded = true ; if ( viewData . status == ViewStatus . WAITTING_CLOSE ) { reslove ( null ) ; if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }${ className }正等待关闭` ) ; viewData . doCallback ( null , className , "获取界内已经关闭" ) ; return ; } let uiNode = cc . instantiate ( data ) ; viewData . node = uiNode ; let view = this . _addComponent ( uiNode , uiClass , viewData , className , zOrder , args , bundle ) ; if ( ! view ) { reslove ( null ) ; return ; } if ( viewData . status == ViewStatus . WATITING_HIDE ) { view . hide ( ) ; if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }加载过程隐藏了界面${ className }` ) ; reslove ( view ) ; viewData . doCallback ( view , className , "加载完成，但加载过程中被隐藏" ) ; } else { if ( CC_DEBUG ) cc . log ( `${ this . _logTag }open view : ${ className }` ) ; if ( ! viewData . isPreload ) { view . show ( args ) ; } reslove ( view ) ; viewData . doCallback ( view , className , "加载完成，回调之前加载中的界面" ) ; } } private loadPrefab ( bundle , url , progressCallback ) { return new Promise < cc . Prefab > ( ( resolove , reject ) => { if ( bundle == undefined || bundle == "" || bundle == null ) { bundle = BUNDLE_RESOURCES ; } Manager . assetManager . load ( bundle , url , cc . Prefab , progressCallback , ( data ) => { if ( data && data . data && data . data instanceof cc . Prefab ) { resolove ( data . data ) ; } else { reject ( `加载prefab : ${ url } 失败` ) ; } } ) ; } ) ; } public getCanvas ( ) { let rootScene = cc . director . getScene ( ) ; if ( ! rootScene ) { if ( CC_DEBUG ) cc . error ( `${ this . _logTag }当前场景为空 ： ${ cc . director . getScene ( ) . name }` ) ; return null ; } let root = rootScene . getChildByName ( "Canvas" ) ; if ( ! root ) { if ( CC_DEBUG ) cc . error ( `${ this . _logTag }当前场景上找不到 Canvas 节点` ) ; return null ; } return root ; } public onDirectorAfterDraw ( ) { let cando = true ; let hasWaitingClose = false ; this . _viewDatas . forEach ( ( viewData ) => { if ( viewData ) { if ( ! viewData . isLoaded ) cando = false ; if ( viewData . status == ViewStatus . WAITTING_CLOSE ) hasWaitingClose = true ; } } ) ; if ( hasWaitingClose && cando ) { cc . time ( "释放资源" ) ; this . _viewDatas . forEach ( ( viewData , className ) => { if ( viewData && viewData . status == ViewStatus . WAITTING_CLOSE ) { cc . time ( `${ this . _logTag } close view : ${ className }` ) ; if ( cc . isValid ( viewData . node ) ) { viewData . node . removeFromParent ( false ) ; viewData . node . destroy ( ) ; } viewData . loadData . clear ( ) ; Manager . assetManager . releaseAsset ( viewData . info ) ; this . _viewDatas . delete ( className ) ; cc . timeEnd ( `${ this . _logTag } close view : ${ className }` ) ; } } ) ; this . garbage . clear ( ) ; cc . timeEnd ( "释放资源" ) ; } if ( this . _isNeedGC ) { let now = Date . timeNowMillisecons ( ) ; if ( now - this . _lastGCTime > this . GC_INTERVAL ) { cc . sys . garbageCollect ( ) ; if ( CC_DEBUG ) cc . log ( `GC内存操作` ) ; this . _isNeedGC = false ; } } return cando ; } description public addLocal ( info , className ) { if ( info ) { let viewData = this . getViewData ( className ) ; if ( viewData ) { viewData . loadData . addLocal ( info , className ) ; } } } description public addRemote ( info , className ) { if ( info ) { let viewData = this . getViewData ( className ) ; if ( viewData ) { viewData . loadData . addRemote ( info , className ) ; } } } public recordGCTime ( isNeedGC = null ) { if ( isNeedGC != null ) this . _isNeedGC = isNeedGC ; this . _lastGCTime = Date . timeNowMillisecons ( ) ; } public close < T extends UIView > ( uiClass ) ; public close ( className ) ; public close ( data ) { let viewData = this . getViewData ( data ) ; if ( viewData ) { viewData . status = ViewStatus . WAITTING_CLOSE ; if ( viewData . view && cc . isValid ( viewData . node ) ) { viewData . node . removeFromParent ( false ) ; } this . recordGCTime ( ) ; } } description public closeExcept ( views ) { let self = this ; if ( views == undefined || views == null || views . length == 0 ) { if ( CC_DEBUG ) cc . error ( `请检查参数，至少需要保留一个界面，不然就黑屏了，大兄弟` ) ; this . _viewDatas . forEach ( ( viewData , key ) => { self . close ( key ) ; } ) ; return ; } let viewClassNames = new Set < string > ( ) ; for ( let i = 0 ; i < views . length ; i ++ ) { viewClassNames . add ( this . getClassName ( views [ i ] as any ) ) ; } this . _viewDatas . forEach ( ( viewData , key ) => { if ( viewClassNames . has ( key ) ) { return ; } self . close ( key ) ; } ) ; this . printViews ( ) ; } public hide ( className ) ; public hide < T extends UIView > ( uiClass ) ; public hide ( data ) { let viewData = this . getViewData ( data ) ; if ( viewData ) { if ( viewData . isLoaded ) { if ( viewData . view && cc . isValid ( viewData . view . node ) ) { viewData . view . hide ( ) ; } if ( CC_DEBUG ) cc . log ( `${ this . _logTag }hide view : ${ viewData . loadData . name }` ) ; } else { viewData . status = ViewStatus . WATITING_HIDE ; } } } public getView ( className ) ; public getView < T extends UIView > ( uiClass ) ; public getView ( data ) { return new Promise < any > ( ( resolove , reject ) => { if ( data == undefined || data == null ) { resolove ( null ) ; return ; } let viewData = this . getViewData ( data ) ; if ( viewData ) { if ( viewData . isPreload ) { resolove ( null ) ; } else { if ( viewData . isLoaded ) { resolove ( viewData . view ) ; } else { viewData . getViewCb . push ( resolove ) ; } } } else { resolove ( null ) ; } } ) ; } public checkView ( url , className ) { if ( CC_DEBUG && className ) { this . getView ( className ) . then ( ( view ) => { if ( ! view ) { let viewData = this . getViewData ( className ) ; if ( viewData ) { if ( ! viewData . isPreload ) { cc . error ( `资源 : ${ url } 的持有者必须由UIManager.open方式打开` ) ; } } else { cc . error ( `资源 : ${ url } 的持有者必须由UIManager.open方式打开` ) ; } } } ) ; } } public isShow ( className ) ; public isShow < T extends UIView > ( uiClass ) ; public isShow ( data ) { let viewData = this . getViewData ( data ) ; if ( ! viewData ) { return false ; } if ( viewData . isLoaded && viewData . status == ViewStatus . WAITTING_NONE ) { if ( viewData . view ) return viewData . view . node . active ; } return false ; } public fullScreenAdapt ( ) { this . _viewDatas . forEach ( ( data ) => { if ( data . isLoaded && data . view ) { Manager . resolutionHelper . fullScreenAdapt ( data . view . node ) ; } } ) ; } public getCanvasComponent ( ) { return this . getCanvas ( ) . getComponent ( "MainController" ) ; } public addComponent < T extends cc . Component > ( type ) ; public addComponent ( className ) ; public addComponent ( data ) { let canvas = this . getCanvas ( ) ; if ( canvas ) { let component = canvas . getComponent ( data ) ; if ( component ) { if ( typeof data == "string" ) { if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }已经存在 Component ${ component }` ) ; } else { if ( CC_DEBUG ) cc . warn ( `${ this . _logTag }已经存在 Component ${ cc . js . getClassName ( data ) }` ) ; } return component ; } else { return canvas . addComponent ( data ) ; } } return null ; } public removeComponent ( component ) { let canvas = this . getCanvas ( ) ; if ( canvas ) canvas . removeComponent ( component ) ; } public printViews ( ) { cc . log ( `${ this . _logTag }---------views----start-----` ) ; this . _viewDatas . forEach ( ( value , key ) => { cc . log ( `[${ key }] isLoaded : ${ value . isLoaded } status : ${ value . status } view : ${ value . view } active : ${ value . view && value . view . node ? value . view . node . active : false }` ) ; } ) ; cc . log ( `${ this . _logTag }---------views----end-----` ) ; } public printCanvasChildren ( ) { cc . log ( `${ this . _logTag }-----------printCanvasChildren--start-----------` ) ; let canvas = this . getCanvas ( ) ; if ( canvas ) { let children = canvas . children ; for ( let i = 0 ; i < children . length ; i ++ ) { cc . log ( `${ children [ i ] . name } active : ${ children [ i ] . active }` ) ; } } cc . log ( `${ this . _logTag }-----------printCanvasChildren--end-----------` ) ; } }