using System; using Cysharp.Threading.Tasks; using DG.Tweening; using Framework; using PhxhSDK; using UnityEngine; using UnityEngine.UI; /// /// UI窗口基类 - 生命周期管理部分 /// /// 功能概述: /// - 窗口生命周期管理(Init → Show → Update → Hide → Close) /// - 资源加载与释放管理 /// - 屏幕适配与相机控制 /// - 动画播放与UI事件处理 /// /// 设计特点: /// - Init和Show分离设计,支持窗口预初始化 /// - 完整的生命周期钩子函数,便于子类扩展 /// - 异常安全的显示/隐藏逻辑 /// - 自动资源管理和内存清理 /// public abstract partial class UIWindow { /* ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ UIWindow 生命周期 (Lifecycle) ║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ 1. 【Load 阶段】- 资源加载 ║ ║ UIRoot.CreateWindow() ║ ║ ├─ 加载预制体资源 ║ ║ ├─ 实例化GameObject ║ ║ └─ 创建UIWindow实例 ║ ║ ║ ║ 2. 【Init 阶段】- 初始化 ║ ║ UIWindow.Init(data) // 窗口初始化(独立调用) ║ ║ ├─ *OnBeforeInit(data) // 预处理数据 (原 PreLoad) ║ ║ ├─ DoInit() // 基础初始化 ║ ║ │ ├─ AutoBindField() // 绑定UI组件引用 ║ ║ │ ├─ InitScreenAdaption() // 屏幕适配 ║ ║ │ └─ _TryBindBgClose() // 绑定通用事件 ║ ║ └─ *OnInit() // 初始化UI状态,设置默认值 ║ ║ ║ ║ 3. 【Show 阶段】- 显示 ║ ║ UIWindow.ShowWindow(data) // 显示窗口(公共接口) ║ ║ ├─ DoShow() // 处理相机状态等显示逻辑 ║ ║ ├─ *OnShow(data) // 显示时回调,业务逻辑处理 ║ ║ ├─ SetActive(true) // 激活GameObject ║ ║ ├─ *OnAfterShow(data) // 显示后回调,最终处理 ║ ║ └─ 发送UI打开事件 // EventManager.Send ║ ║ 4. 【Update 阶段】- 运行时更新 ║ ║ Update(dt) -> *OnUpdate(dt) // 每帧更新,处理UI逻辑(合并原 Update) ║ ║ ║ ║ 5. 【Refresh 阶段】- 数据刷新 ║ ║ RefreshWithData(data) // 数据刷新(原 ReloadWindow) ║ ║ └─ *OnDataRefresh(data) // 刷新UI内容,更新显示数据(原 OnReloadWindow) ║ ║ ║ ║ 6. 【Hide 阶段】- 隐藏 ║ ║ HideWindow() -> Hide() ║ ║ ├─ SetActive(false) // 隐藏GameObject ║ ║ └─ *OnHide() // 隐藏回调 ║ ║ ║ ║ 7. 【Close 阶段】- 关闭销毁 ║ ║ CloseWindow() -> DestroyWindow() ║ ║ ├─ *OnHide() // 隐藏回调 ║ ║ ├─ *OnRelease() // 释放资源,清理引用 ║ ║ ├─ Destroy(gameObject) // 销毁GameObject ║ ║ └─ 卸载资源 ║ ║ ║ ║ 【当前架构特点】 ║ ║ • 分离式设计: Init和Show独立调用,支持预初始化窗口 ║ ║ • 使用方式: 先调用Init(data)初始化,再调用ShowWindow(data)显示 ║ ║ • 灵活性: 可以先初始化多个窗口,根据需要选择性显示 ║ ║ • 性能优化: 避免显示时的初始化卡顿,提升用户体验 ║ ║ ║ ║ 【子类需要关注的虚函数 - 标记为 * 的函数】 ║ ║ ║ ║ 🔸 *OnBeforeInit(data) - 【可选重写】数据预处理 ║ ║ • 时机:在 DoInit(AutoBindField、InitScreenAdaption)和 OnInit 之前调用 ║ ║ • 用途:解析传入数据,设置初始状态,准备UI显示内容 ║ ║ • 示例:解析OpenData,设置默认选中项,预加载必要资源 ║ ║ • 注意:不需要调用 base.OnBeforeInit() ║ ║ ║ ║ 🔸 *OnInit() - 【常用重写】UI初始化 ║ ║ • 时机:在 DoInit(基础初始化)之后,窗口显示之前调用 ║ ║ • 用途:初始化UI组件,绑定事件,设置默认值 ║ ║ • 示例:按钮绑定,列表初始化,默认文本设置 ║ ║ • 注意:不需要调用 base.OnInit() ║ ║ ║ ║ 🔸 *OnShow(data) - 【常用重写】显示时业务逻辑 ║ ║ • 时机:窗口显示过程中调用,在SetActive之前 ║ ║ • 用途:处理显示时的主要业务逻辑,数据刷新,UI更新 ║ ║ • 示例:刷新列表数据,更新UI显示,准备动画数据 ║ ║ • 注意:不需要调用 base.OnShow(data) ║ ║ ║ ║ 🔸 *OnAfterShow(data) - 【按需重写】显示后最终处理 ║ ║ • 时机:窗口完全显示后调用,在SetActive之后 ║ ║ • 用途:处理显示完成后的后续逻辑,启动动画,开始计时 ║ ║ • 示例:播放入场动画,开始倒计时,发送显示完成事件 ║ ║ • 注意:不需要调用 base.OnAfterShow(data) ║ ║ ║ ║ 🔸 *OnUpdate(dt) - 【按需重写】每帧更新 ║ ║ • 时机:每帧调用(仅在窗口激活且已创建时) ║ ║ • 用途:处理需要持续更新的逻辑 ║ ║ • 示例:倒计时更新,进度条刷新,实时数据显示 ║ ║ • 注意:避免在此进行重度计算,影响性能 ,不需要调用 base.OnUpdate(dt) ║ ║ ║ ║ 🔸 *OnDataRefresh(data) - 【按需重写】数据刷新 ║ ║ • 时机:调用 RefreshWithData 时触发 ║ ║ • 用途:更新已显示窗口的内容,不重新初始化 ║ ║ • 示例:更新商品列表,刷新玩家信息,切换显示内容 ║ ║ • 注意:通常不需要调用 base.OnDataRefresh(data) ║ ║ ║ ║ 🔸 *OnHide() - 【按需重写】隐藏时清理 ║ ║ • 时机:窗口隐藏时调用 ║ ║ • 用途:暂停逻辑,保存状态,停止动画 ║ ║ • 示例:暂停倒计时,保存输入内容,停止音效 ║ ║ • 注意:不需要调用 base.OnHide() ║ ║ ║ ║ 🔸 *OnRelease() - 【重要重写】资源释放 ║ ║ • 时机:窗口销毁时调用 ║ ║ • 用途:释放资源,取消订阅,清理引用 ║ ║ • 示例:取消事件监听,释放Tween,清理对象池 ║ ║ • 注意:不需要调用 base.OnRelease() ║ ║ ║ ║ 【其他说明】 ║ ║ • AutoBindField: 自动绑定字段,由代码生成工具处理,不要手动重写 ║ ║ • RefreshWithData: 数据刷新函数,窗口已初始化时调用OnDataRefresh,未初始化时报错 ║ ║ • Init: 初始化函数,确保窗口只初始化一次,包含OnBeforeInit、DoInit、OnInit的完整流程 ║ ║ • ShowWindow: 显示窗口的公共接口,内部调用私有的Show函数执行实际显示逻辑 ║ ║ • 架构特点: Init和Show分离,支持窗口预初始化,然后在需要时显示 ║ ║ • 重写虚函数时,不需要调用 base.OnXxx(),具体见上述说明 ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ */ #region Private Helper Methods /// /// 绑定背景关闭按钮 /// private void _TryBindBgClose() { var trans = transform.Find("BgClose"); if (trans) { var btn = trans.GetComponent