NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/UI/UIWindow.Lifecycle.cs

559 lines
34 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using DG.Tweening;
using Framework;
using PhxhSDK;
using UnityEngine.UI;
public abstract partial class UIWindow
{
/*
╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ UIWindow 生命周期 (Lifecycle) ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ 1. 【Load 阶段】- 资源加载 ║
║ UIManager.CreateWindow() ║
║ ├─ 加载预制体资源 ║
║ ├─ 实例化GameObject ║
║ └─ 创建UIWindow实例 ║
║ ║
║ 2. 【Init 阶段】- 初始化 ║
║ UIWindow.Init(data) // 窗口初始化(独立调用) ║
║ ├─ *OnBeforeInit(data) // 预处理数据(不推荐使用) ║
║ ├─ DoInit() // 基础初始化 ║
║ │ ├─ AutoBindField() // 绑定UI组件引用 ║
║ │ ├─ InitScreenAdaption() // 屏幕适配 ║
║ │ └─ _TryBindBgClose() // 绑定背景关闭按钮 ║
║ └─ *OnInit() // 初始化UI状态,设置默认值 ║
║ ║
║ 3. 【Show 阶段】- 显示 ║
║ UIWindow.ShowWindow(data) // 显示窗口(公共接口) ║
║ ├─ *OnBeforeShow(data) // 显示前检查 ║
║ ├─ DoShow() // 基础显示逻辑 ║
║ ├─ *OnShow(data) // 显示时业务逻辑处理 ║
║ ├─ SetActive(true) // 激活GameObject ║
║ ├─ *OnAfterShow(data) // 显示后处理 ║
║ └─ 发送UI打开事件 // EventManager.Send ║
║ ║
║ 4. 【Update 阶段】- 运行时更新 ║
║ Update(dt) -> *OnUpdate(dt) // 每帧更新,处理UI逻辑 ║
║ ║
║ 5. 【Refresh 阶段】- 数据刷新 ║
║ Refresh(data) // 数据刷新(公共接口) ║
║ └─ *OnRefresh(data) // 刷新UI内容,更新显示数据 ║
║ ║
║ 6. 【Hide 阶段】- 隐藏 ║
║ HideWindow() // 隐藏窗口 ║
║ ├─ *OnHide() // 隐藏回调 ║
║ ├─ SetActive(false) // 隐藏GameObject ║
║ └─ 发送UI隐藏事件 // EventManager.Send ║
║ ║
║ 7. 【Close 阶段】- 关闭销毁 ║
║ CloseWindow() -> DestroyWindow() ║
║ ├─ *OnHide() // 隐藏回调 ║
║ ├─ *OnRelease() // 释放资源,清理引用 ║
║ ├─ DoRelease() // 基础资源释放 ║
║ ├─ Destroy(gameObject) // 销毁GameObject ║
║ └─ 卸载预制体资源 // AssetManager.Unload ║
║ ║
║ 【架构特点】 ║
║ • 分离式设计: Init和Show独立调用,支持预初始化窗口 ║
║ • 使用方式: 先调用Init(data)初始化,再调用ShowWindow(data)显示 ║
║ • 灵活性: 可以先初始化多个窗口,根据需要选择性显示 ║
║ • 性能优化: 避免显示时的初始化卡顿,提升用户体验 ║
║ • 数据更新: Show用于显示配置,Refresh用于内容更新,职责分离 ║
║ ║
║ 【虚函数重写指南 - 标记为 * 的函数】 ║
║ ║
║ 🔸 *OnBeforeInit(data) - 【不推荐】数据预处理 ║
║ • 时机:在DoInit和OnInit之前调用 ║
║ • 用途:解析传入数据,设置初始状态 ║
║ • ⚠️ 不建议使用:建议在OnInit中处理所有初始化逻辑 ║
║ • 原因:避免逻辑分散,减少维护复杂度 ║
║ ║
║ 🔸 *OnInit() - 【常用】UI初始化 ║
║ • 时机:在DoInit之后,窗口显示之前 ║
║ • 用途:初始化UI组件,绑定事件,设置默认值 ║
║ • 示例:按钮绑定,列表初始化,默认文本设置 ║
║ ║
║ 🔸 *OnBeforeShow(data) - 【可选】显示前检查 ║
║ • 时机:在DoShow和OnShow之前调用 ║
║ • 用途:显示前的条件检查,返回false可取消显示 ║
║ ║
║ 🔸 *OnShow(data) - 【常用】显示时业务逻辑 ║
║ • 时机:窗口显示过程中,在SetActive之前 ║
║ • 用途:处理显示时的主要业务逻辑,设置显示配置 ║
║ • 示例:设置窗口模式,初始化显示状态 ║
║ • 注意:与Refresh职责分离,Show负责显示配置,Refresh负责内容更新 ║
║ ║
║ 🔸 *OnAfterShow(data) - 【可选】显示后处理 ║
║ • 时机:窗口完全显示后,在SetActive之后 ║
║ • 用途:播放动画,开始计时等后续逻辑 ║
║ ║
║ 🔸 *OnUpdate(dt) - 【可选】每帧更新 ║
║ • 时机:每帧调用(仅在窗口激活时) ║
║ • 用途:倒计时更新,进度条刷新等 ║
║ • 注意:避免重度计算,影响性能 ║
║ ║
║ 🔸 *OnRefresh(data) - 【推荐】数据刷新 ║
║ • 时机:调用Refresh(data)时触发 ║
║ • 用途:更新已显示窗口的内容数据,不重新初始化 ║
║ • 示例:刷新列表数据,更新文本内容,重新计算显示 ║
║ • 设计:与Show分离,Show处理显示配置,Refresh处理内容更新 ║
║ • 调用:只能通过Refresh(data)公共接口调用,不要直接调用OnRefresh ║
║ ║
║ 🔸 *OnHide() - 【可选】隐藏时处理 ║
║ • 时机:窗口隐藏时调用 ║
║ • 用途:暂停逻辑,保存状态,停止动画 ║
║ ║
║ 🔸 *OnRelease() - 【重要】资源释放 ║
║ • 时机:窗口销毁时调用 ║
║ • 用途:释放资源,取消订阅,清理引用 ║
║ • 示例:取消事件监听,释放Tween,清理对象池 ║
║ ║
║ 【重要说明】 ║
║ • 所有虚函数重写时都不需要调用base.OnXxx() ║
║ • AutoBindField由代码生成工具处理,不要手动重写 ║
║ • Init和Show分离设计,支持窗口预初始化 ║
║ • ⚠️ 不建议重写OnBeforeInit,统一在OnInit中处理初始化逻辑 ║
║ • Show与Refresh职责分离:Show负责显示配置,Refresh负责内容更新 ║
║ • 禁止直接调用OnXxx虚方法,必须通过对应的公共接口调用 ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
*/
#region Lifecycle Management
/// <summary>
/// 初始化窗口
/// </summary>
/// <param name="data">初始化数据</param>
public void Init(object data = null)
{
try
{
OnBeforeInit(data);
if (isInited)
{
DebugUtil.LogWarning("window : {0} already inited", WindowName);
}
else
{
DoInit();
OnInit();
isInited = true;
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} init window error: {1}", WindowName, e.Message);
}
}
/// <summary>
/// 显示窗口
/// </summary>
/// <param name="data">显示数据</param>
public void ShowWindow(object data = null)
{
// 未被唤醒不执行Show
if (IsUnEnabled)
{
return;
}
try
{
if (_isShow)
{
DebugUtil.LogWarning("window : {0} is show, can't show again", WindowName);
}
if (!OnBeforeShow(data))
{
DebugUtil.LogError("window : {0} on before show return false", WindowName);
return;
}
DoShow();
OnShow(data);
if (gameObject == null)
{
DebugUtil.Log("UI已被销毁:{0}", WindowName);
}
else
{
gameObject.SetActive(true);
}
_isShow = true;
OnAfterShow(data);
EventManager.Instance.Send(EventManager.EventName.UIOpen, WindowName);
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} show window error: {1}", WindowName, e.Message);
}
}
/// <summary>
/// 更新入口
/// </summary>
public void Update(float dt)
{
try
{
_notchScreenAdapter?.UpdateCheck();
OnUpdate(dt);
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} update window error: {1}", WindowName, e.Message);
}
}
/// <summary>
/// 刷新窗口数据
/// </summary>
/// <param name="data">新数据</param>
public void Refresh(object data)
{
try
{
if (isInited)
{
OnRefresh(data);
}
else
{
DebugUtil.LogError("window : {0} not inited", WindowName);
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} refresh with data error: {1}", WindowName, e.Message);
}
}
/// <summary>
/// 隐藏窗口
/// </summary>
public void HideWindow()
{
if (!_isShow)
{
DebugUtil.LogWarning("window : {0} is hide, can't hide again", WindowName);
}
try
{
if (UIManager.Instance.WindowInfos.TryGetValue(WindowName, out var windowInfo))
{
if (windowInfo.UIOpenType == UIOpenType.FullScreen)
{
if (!CameraManager.Instance.CheckMainCameraIsActive())
{
CameraManager.Instance.SetMainCameraActive(true);
}
}
}
OnHide();
gameObject?.SetActive(false);
_isShow = false;
EventManager.Instance.Send(EventManager.EventName.UIHide, WindowName);
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} hide window error: {1}", WindowName, e.Message);
}
}
/// <summary>
/// 关闭窗口
/// </summary>
/// <param name="closeType">关闭类型</param>
public async void CloseWindow(UIWindowCloseType closeType)
{
try
{
IsClosing = true;
DebugUtil.LogG($"关闭UI:{WindowName}, closeType{closeType}");
if (UIManager.Instance.CheckWindowIsFullScreen(this))
{
if (_shutMainCamera)
{
CameraManager.Instance.SetMainCameraActive(true);
}
}
if (closeType == UIWindowCloseType.HideAndDestroy)
{
var last = UIManager.Instance.GetLast();
if (last == this)
{
DebugUtil.LogG($"PopWindow{this.WindowName}");
UIManager.Instance.PopWindow();
}
if (UIManager.Instance.UiWindowStack.Count > 0)
{
if (UIManager.Instance.CheckWindowIsFullScreen(this))
{
var window = UIManager.Instance.GetLastFullScreen();
if (window != null && window != this)
{
if (UIManager.Instance.CheckIsWindowInStack(this))
{
UIManager.Instance.ForceRemoveWindow(this);
DebugUtil.LogG($"UI出栈,{WindowName}");
}
}
for (int i = UIManager.Instance.UiWindowStack.Count - 1; i >= 0; i--)
{
var latestWindow = UIManager.Instance.UiWindowStack[i];
latestWindow.ShowWindow();
if (UIManager.Instance.CheckWindowIsFullScreen(latestWindow))
break;
}
}
else
{
UIManager.Instance.ForceRemoveWindow(this);
}
}
bool isShowCloseAnim = IsShowOpenAnim;
if (isShowCloseAnim)
{
UIManager.Instance.BlockUIForCloseOperation();
await AnimHide(() =>
{
DestroyWindow();
UIManager.Instance.RemoveWindow(mWindowName);
IsClosing = false;
});
}
else
{
UIManager.Instance.RemoveWindow(mWindowName);
DestroyWindow();
IsClosing = false;
}
}
else if (closeType == UIWindowCloseType.ForceDestroy)
{
// 强制清除所有界面时使用
DestroyWindow();
UIManager.Instance.RemoveWindow(mWindowName);
IsClosing = false;
}
}
catch (System.Exception e)
{
DebugUtil.LogError("window : {0} close window error: {1}", WindowName, e.Message);
}
}
#endregion
#region Internal Implementation
/// <summary>
/// 执行基础初始化
/// </summary>
private void DoInit()
{
_isShow = false;
_shutMainCamera = UIManager.Instance.IsShutMainCamera(WindowName);
IsShowOpenAnim = UIManager.Instance.GetIsAutoPlayAnim(WindowName);
AutoBindField();
InitScreenAdaption();
_TryBindBgClose();
}
/// <summary>
/// 执行显示逻辑
/// </summary>
private void DoShow()
{
if (UIManager.Instance.WindowInfos.TryGetValue(WindowName, out var windowInfo))
{
if (CameraManager.Instance.CheckMainCameraIsActive())
{
if (windowInfo.UIOpenType == UIOpenType.FullScreen)
{
// 特殊处理主界面
if (_shutMainCamera && WindowName != UINameConst.UI_MainPanel)
{
CameraManager.Instance.SetMainCameraActive(false);
}
}
}
}
}
/// <summary>
/// 销毁窗口
/// </summary>
private void DestroyWindow()
{
DebugUtil.LogG($"DestroyWindow:{WindowName}");
OnHide();
gameObject?.SetActive(false);
_isShow = false;
EventManager.Instance.Send(EventManager.EventName.UIHide, WindowName);
if (gameObject)
{
if (isInited)
{
OnRelease();
DoRelease();
isInited = false;
}
if (UIManager.Instance.UIWindowObjDic.ContainsKey(gameObject))
{
UIManager.Instance.UIWindowObjDic.Remove(gameObject);
}
UnityEngine.Object.Destroy(gameObject);
}
AssetManager.Instance.Unload(string.Format(UIManager.UI_PREFAB_PATH, mWindowName), true);
gameObject = null;
}
/// <summary>
/// 基础资源释放
/// </summary>
private void DoRelease()
{
EventManager.Instance.Send(EventManager.EventName.UIDestroy, WindowName);
UnLoad();
_openAnimSeq?.Kill();
_assetReferenceManager?.Dispose();
_assetReferenceManager = null;
_notchScreenAdapter = null;
_graphicRaycasterManager = null;
}
/// <summary>
/// 绑定背景关闭按钮
/// </summary>
private void _TryBindBgClose()
{
var trans = transform.Find("BgClose");
if (trans)
{
var btn = trans.GetComponent<Button>();
if (btn)
{
BindButton(btn, () =>
{
var clickAudio = btn.GetComponent<ClickAudio>();
if (clickAudio)
{
if (clickAudio.IsPlayAudio && !string.IsNullOrEmpty(clickAudio.AudioKey))
{
AudioManager.Instance.PlayAudio(clickAudio.AudioKey);
}
else
{
CommonUtilsFramework.PlayUICommonSound();
}
}
if (_onBgCloseFunc == null)
{
CloseWindow(UIWindowCloseType.HideAndDestroy);
return;
}
_onBgCloseFunc?.Invoke();
});
}
}
}
#endregion
#region Virtual Methods
// Init Phase
/// <summary>
/// 初始化前预处理(不推荐使用,建议在OnInit中处理)
/// </summary>
protected virtual void OnBeforeInit(object data = null){}
/// <summary>
/// UI初始化
/// </summary>
protected virtual void OnInit(){}
/// <summary>
/// 自动绑定字段
/// </summary>
protected virtual void AutoBindField(){}
// Show Phase
/// <summary>
/// 显示前检查
/// </summary>
protected virtual bool OnBeforeShow(object data)
{
return true;
}
/// <summary>
/// 显示时处理
/// </summary>
protected virtual void OnShow(object data){}
/// <summary>
/// 显示后处理
/// </summary>
protected virtual void OnAfterShow(object data){}
/// <summary>
/// 每帧更新
/// </summary>
protected virtual void OnUpdate(float dt){}
// Refresh Phase
/// <summary>
/// 数据刷新
/// </summary>
protected virtual void OnRefresh(object data) {}
// Hide Phase
/// <summary>
/// 隐藏时处理
/// </summary>
protected virtual void OnHide(){}
// Release Phase
/// <summary>
/// 资源释放
/// </summary>
protected virtual void OnRelease(){}
#endregion
}