using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Cysharp.Threading.Tasks; using Framework; using Obfuz; using PhxhSDK; using PhxhSDK.AOT.Aspect; using UnityEngine; [ObfuzIgnore] public partial class UIManager : Singlenton, IInitable, IUpdatable { public const string UI_PREFAB_PATH = "Assets/Art_Out/UI/Prefab/{0}.prefab"; public Dictionary mMemoryWindows = new Dictionary(); // ✅ 使用独立的层级管理器 private UICanvasLayerManager LayerManager => UICanvasLayerManager.Instance; private ScreenOrientation _currentOrientation; private Dictionary _openingWindows = new Dictionary(); private List _hideCache = new List(); private UIWindow _curFullScreen; private Dictionary _uiWindowObjDic = new Dictionary(); public Dictionary UIWindowObjDic => _uiWindowObjDic; public Vector2 Size; /// /// UI和主相机屏幕缩放 /// public Vector2 UIAndMainScreenScale; /// /// UI和UI屏幕缩放 /// public Vector2 UIAndUIScreenScale; /// /// 禁用返回键计数 /// private int disableBackCount; /// /// 是否响应返回键 /// public bool IsEnableBack { get { return disableBackCount <= 0; } set { if (value) { if (disableBackCount > 0) --disableBackCount; } else ++disableBackCount; } } public void Init() { IsEnableBack = true; _AllWindowMeta(); // 初始化层级管理器 LayerManager.Initialize(UIRoot.Instance.GetAllLayerRoots()); if (DeviceHelper.IsFullScreenIOS()) { SetFullScreenIOS(); } EventManager.Instance.Register(EventManager.EventName.Android_Click_Back, _OnAndroidClickBack); EventManager.Instance.Register(EventManager.EventName.ClearUIStack, (Action)ClearHideStack); OnScreenSizeChange(); AspectManager.Instance.ScreenSizeChange += OnScreenSizeChange; } public void Release() { LayerManager.Clear(); AspectManager.Instance.ScreenSizeChange -= OnScreenSizeChange; EventManager.Instance.Unregister(EventManager.EventName.Android_Click_Back, _OnAndroidClickBack); EventManager.Instance.Unregister(EventManager.EventName.ClearUIStack, (Action)ClearHideStack); } public bool disableUILogic = false; public void Update(float dt) { if (disableUILogic) return; foreach (var winKv in mMemoryWindows) { var window = winKv.Value; if (window != null && !window.IsClosing) { if (window.IsCreated() && window.gameObject) { if (window.gameObject) { window.Update(dt); } } } } _UpdateInputBlockTimer(dt); } #region UIWindow Manager public static void BindWindowObj(UIWindow window, GameObject uiObj) { if (window == null || uiObj == null) { if (window == null) DebugUtil.LogError("window is null"); if (uiObj == null) DebugUtil.LogError("uiObj is null"); return; } window.gameObject = uiObj.gameObject; window.transform = uiObj.transform; window.name = uiObj.name; } #endregion #region OpenWindowAPI /// /// 打开一个UI /// UIManager.Instance.OpenWindow("Common/GameUI") /// /// /// 创建并打开窗口 /// /// 窗口路径 /// 传递的数据 /// 覆盖栈模式(null则使用配置)。⚠️ 谨慎使用,大部分情况应使用配置 public async UniTask CreateAndOpenWindow(string path, object data = null, UIStackMode? overrideStackMode = null) { DebugUtil.LogP($"打开窗口:{path}"); if (mMemoryWindows.ContainsKey(path)) { DebugUtil.LogG($"窗口已打开!!!{path}"); //return null; } var window = await _LoadWindow(path, true); if (window != null) { // 初始化和显示窗口 window.Init(data); window.ShowWindow(data); // 获取配置 var config = GetWindowInfo(path); var stackMode = overrideStackMode ?? config.StackMode; // 1. 栈管理 UINavigationManager.Instance.HandleWindowOpen(window, stackMode); // 2. 可见性管理 UIWindowVisibilityManager.Instance.HandleVisibilityOnOpen(window, config.VisibilityStrategy); } return window; } public bool Back() { return false; } public bool RemoveWindow(string wName) { string windowName = wName; bool result = false; UIWindow window; if (mMemoryWindows.TryGetValue(windowName, out window)) { LayerManager.RemoveWindow(window); mMemoryWindows.Remove(windowName); result = true; } return result; } /// /// 关闭窗口 /// /// 窗口名称 /// 是否销毁窗口 public void CloseWindow(string wName, bool destroy) { var window = GetOpenedWindowByPath(wName); if (window != null) { window.CloseWindow(destroy); } } /// /// 打开已加载的窗口(显示并根据配置处理导航栈) /// 适用于预加载窗口的显示场景 /// /// 已加载的窗口 /// 传递的数据(可选) /// 覆盖栈模式(null则使用配置) public void OpenLoadedWindow(UIWindow window, object data = null, UIStackMode? overrideStackMode = null) { if (window == null) { DebugUtil.LogError("OpenLoadedWindow: window is null"); return; } // 显示窗口 window.ShowWindow(data); // 获取配置 var config = GetWindowInfo(window.WindowName); var stackMode = overrideStackMode ?? config.StackMode; // 1. 栈管理 UINavigationManager.Instance.HandleWindowOpen(window, stackMode); // 2. 可见性管理 UIWindowVisibilityManager.Instance.HandleVisibilityOnOpen(window, config.VisibilityStrategy); } /// /// 打开已加载的窗口(通过路径) /// public void OpenLoadedWindow(string path, object data = null, UIStackMode? overrideStackMode = null) { var window = GetOpenedWindowByPath(path); if (window != null) { OpenLoadedWindow(window, data, overrideStackMode); } else { DebugUtil.LogError($"OpenLoadedWindow: window not found: {path}"); } } public T GetOpenedWindowByPath(string uipath) where T : UIWindow { var window = GetOpenedWindowByPath(uipath) as T; //return window.gameObject.activeSelf ? window : null; return window; } public UIWindow GetOpenedWindowByPath(string uipath) { if (string.IsNullOrEmpty(uipath)) { DebugUtil.LogError("UIManager.GetOpenedWindowByPath, uipath is null or empty"); return null; } if (mMemoryWindows.ContainsKey(uipath)) { //return window.gameObject.activeSelf ? window : null; return mMemoryWindows[uipath]; } return null; } public bool GetTopWindow(out UIWindow window, out UICanvasLayer layer, int banLayerMask) { return LayerManager.GetTopWindow(out window, out layer, banLayerMask); } public int LayerMask(UICanvasLayer layer) { return LayerManager.GetLayerMask(layer); } async void SetFullScreenIOS() { await UniTask.Delay(670); // while (true) // { // var screenOrientation = DragonNativeBridge.GetScreenOrientation(); // if (screenOrientation != _currentOrientation) // { // _currentOrientation = screenOrientation; // EventDispatcher.Instance.DispatchEvent(new ScreenOrientationChangedEvent(_currentOrientation)); // } // yield return wait; // } } /// /// 加载窗口(不显示) /// 用于预加载场景 /// public async UniTask LoadWindow(string path) { return await _LoadWindow(path, false); } private async UniTask _LoadWindow(string path, bool isActive = false) { string uipath = path; UIWindow window = null; WindowInfo wInfo; if (!_windowsInfo.ContainsKey(uipath)) { //if (_windowsInfo.TryGetValue(uipath, out wInfo)) { DebugUtil.LogError("Create ui fail: {0}, no windows info inited", uipath); return window; } } wInfo = _windowsInfo[uipath]; var uiWindowLayer = wInfo.windowLayer; if (!mMemoryWindows.TryGetValue(uipath, out window)) { if (_openingWindows.ContainsKey(uipath)) { return window; } _openingWindows.Add(uipath, uipath); window = await UIRoot.Instance.CreateWindow(uipath, uiWindowLayer); _openingWindows.Remove(uipath); if (window != null) { mMemoryWindows[uipath] = window; window.WindowName = uipath; window.uiWindowLayer = uiWindowLayer; } else { DebugUtil.LogError("Create ui fail: {0}", uipath); return window; } } LayerManager.AddWindow(window); if (window == null) { DebugUtil.LogError("Create ui fail: {0}", uipath); return null; } if (!isActive) { window.gameObject.SetActive(false); } _uiWindowObjDic.TryAdd(window.gameObject, window); return window; } //展示全部已加载的NormalUI public void OpenLoadedNormalUI(string uiName, object data = null) { WindowInfo wInfo; wInfo = _windowsInfo[uiName]; if (mMemoryWindows.ContainsKey(uiName)) { mMemoryWindows[uiName].Init(data); mMemoryWindows[uiName].ShowWindow(data); } } public void ForeachUI(Action action) { var windowList = mMemoryWindows.ToList(); foreach (var kv in windowList) { var window = kv.Value; if (window != null) { action?.Invoke(kv.Value); } } } public void CloseAllUIExcept(string path) { var windowList = mMemoryWindows.ToList(); foreach (var kv in windowList) { var window = kv.Value; if(window.WindowName != path) { window.CloseWindow(destroy: true); mMemoryWindows.Remove(window.WindowName); LayerManager.RemoveWindow(window); } } ClearHideStack(); } #endregion #region ShowAndHide /// /// 隐藏所有UI /// 和 ShowAllUI 成对调用 /// /// 不隐藏的UI窗口名称列表 public void HideAllUI(params string[] exceptions) { HideAllUI(UICanvasLayer.Max, exceptions); } /// /// 隐藏指定层级及以下的所有UI /// 和 ShowAllUI 成对调用 /// /// 最大层级(包含该层级及以下的所有UI都会被隐藏) /// 不隐藏的UI窗口名称列表 public void HideAllUI(UICanvasLayer maxLayer, params string[] exceptions) { var hiddenWindows = LayerManager.HideWindowsUpToLayer(maxLayer, exceptions); _hideCache.AddRange(hiddenWindows); } /// /// 隐藏指定层级列表中的所有UI /// 和 ShowAllUI 成对调用 /// /// 要隐藏的层级列表 /// 不隐藏的UI窗口名称列表 public void HideAllUI(UICanvasLayer[] layers, params string[] exceptions) { var hiddenWindows = LayerManager.HideWindowsInLayers(layers, exceptions); _hideCache.AddRange(hiddenWindows); } /// /// 和HideAllUI成对调用 /// public void ShowAllUI() { foreach (var v in _hideCache) { var window = v; if (window != null) window.ShowWindow(); } _hideCache.Clear(); } public async void ShowClickShield(string uiPath, int time = 0) { //放到最上层 if (!_windowsInfo.ContainsKey(uiPath)) { return; } CreateAndOpenWindow(uiPath); mMemoryWindows.TryGetValue(uiPath, out var window); if (time > 0) { await UniTask.Delay(time); window.CloseWindow(destroy: true); } } public void CloseClickShield(string uiPath) { if (mMemoryWindows.ContainsKey(uiPath)) { var window = mMemoryWindows[uiPath]; window.CloseWindow(destroy: true); } } #endregion //根据路径判断UI界面是否打开 public bool IsWindowOpening(string uiPath) { string windowName; if (_openingWindows.TryGetValue(uiPath, out windowName)) { if (windowName != null) { return true; } } return false; } /// /// 关掉所有Normal层已打开的界面,保留传参的界面 /// /// public void CloseAllNormalExcept(string path, bool isShowAnim = true) { var windowsL = LayerManager.GetWindowsInLayer(UICanvasLayer.Normal); if (!mMemoryWindows.ContainsKey(path)) { DebugUtil.LogError("请检查Home返回方法传入UI窗口路径!!!参数:{0}", path); return; } List toBeRemoved = new List(); if (windowsL.Count > 0) { var node = windowsL.First; while (node != null) { var window = node.Value; if (window.WindowName != path) { toBeRemoved.Add(window); } if (node == windowsL.Last) { break; } node = node.Next; } } string[] strL = { path }; ClearHideStack(strL); if (toBeRemoved.Count > 0) { for (int i = toBeRemoved.Count - 2; i >= 0; i--) { toBeRemoved[i].CloseWindow(destroy: true); } } if (UINavigationManager.Instance.IsWindowInStack(mMemoryWindows[path])) { mMemoryWindows[path].ShowWindow(); } } /// /// 打开指定界面,并关闭其他界面 /// /// /// 如果只需要关闭界面,请使用ClearAllNormalExcept方法 public void CloseAllNormalExcept(params string[] paths) { bool isShowAnim = true; var windowsL = LayerManager.GetWindowsInLayer(UICanvasLayer.Normal); foreach (var path in paths) { if (!mMemoryWindows.ContainsKey(path)) { DebugUtil.LogError("请检查Home返回方法传入UI窗口路径!!!参数:{0}", path); return; } } List toBeRemoved = new List(); if (windowsL.Count > 0) { var node = windowsL.First; while (node != null) { var window = node.Value; if (!paths.Contains(window.WindowName)) { toBeRemoved.Add(window); } if (node == windowsL.Last) { break; } node = node.Next; } } //string[] strL = { path }; ClearHideStack(paths); if (toBeRemoved.Count > 0) { for (int i = toBeRemoved.Count - 2; i >= 0; i--) { toBeRemoved[i].CloseWindow(destroy: true); } } foreach (var path in paths) { if (UINavigationManager.Instance.IsWindowInStack(mMemoryWindows[path])) { mMemoryWindows[path].ShowWindow(); } } } /// /// 清理除指定UI以外的界面(只包含关闭操作) /// /// public void ClearAllNormalExcept(params string[] paths) { bool isShowAnim = true; var windowsL = LayerManager.GetWindowsInLayer(UICanvasLayer.Normal); foreach (var path in paths) { if (!mMemoryWindows.ContainsKey(path)) { DebugUtil.LogError("请检查Home返回方法传入UI窗口路径!!!参数:{0}", path); return; } } List toBeRemoved = new List(); if (windowsL.Count > 0) { var node = windowsL.First; while (node != null) { var window = node.Value; if (!paths.Contains(window.WindowName)) { toBeRemoved.Add(window); } if (node == windowsL.Last) { break; } node = node.Next; } } ClearHideStack(paths); if (toBeRemoved.Count > 0) { for (int i = toBeRemoved.Count - 2; i >= 0; i--) { toBeRemoved[i].CloseWindow(destroy: true); } } } private async void _OnAndroidClickBack() { if (!IsEnableBack) return; var topWindow = UINavigationManager.Instance.GetTopWindow(); if (topWindow != null && await topWindow.OnBack()) { return; } if (!CheckWindowCreated(UINameConst.UIBackReturn)) { await CreateAndOpenWindow(UINameConst.UIBackReturn); } } public bool CheckWindowCreated(string uiPath) { if (mMemoryWindows.TryGetValue(uiPath, out var window)) { return window.IsCreated(); } return false; } private async void OnScreenSizeChange() { await UniTask.NextFrame(); // 必须等待一帧,等RectTransform大小刷新后获取 RectTransform rectTrans = UIRoot.Instance.Root.GetComponent(); Size = new(rectTrans.sizeDelta.x, rectTrans.sizeDelta.y); UIAndMainScreenScale = new Vector3(Size.x / Screen.width, Size.y / Screen.height); UIAndUIScreenScale = new Vector3(Size.x / AspectManager.Instance.ScreenSize.x, Size.y / AspectManager.Instance.ScreenSize.y); } #region Navigation Stack Management /// /// 清空导航栈(保留指定窗口) /// public void ClearHideStack(params string[] windowName) { var stack = UINavigationManager.Instance.NavigationStack; // 倒序遍历,避免索引问题 for (int i = stack.Count - 1; i >= 0; i--) { var window = stack[i]; // 检查是否是要保留的窗口 bool shouldKeep = false; for (int j = 0; j < windowName.Length; j++) { if (window.WindowName == windowName[j]) { shouldKeep = true; break; } } if (!shouldKeep) { // 先从导航栈移除,再销毁窗口 UINavigationManager.Instance.HandleWindowClose(window); window.CloseWindow(destroy: true); } } } /// /// 清空所有导航栈 /// public void ClearHideStack() { var stack = UINavigationManager.Instance.NavigationStack; // 先复制列表,避免在遍历时修改集合 var windowsToClose = new List(stack); UINavigationManager.Instance.Clear(); // 再关闭所有窗口 for (int i = 0; i < windowsToClose.Count; i++) { windowsToClose[i].CloseWindow(destroy: true); } } #endregion }