Files
UnityAI/modules/frameworks/UI窗口系统/开发指南.md

4.9 KiB
Raw Blame History

UI 开发指南

一、窗口开发

窗口类继承 AbsHFUIWindow,通过 attribute 声明资源路径和 Canvas 层级:

[HFAssetBundlePath("UIWindows/ExampleWindow/ExampleWindow.prefab")]
[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic, isUnloadOnClose = true)]
public partial class ExampleWindow : AbsHFUIWindow
{
    protected override void OnRunAwake()
    {
        base.OnRunAwake();
        // 查找控件、绑定按钮、初始化列表。
    }

    protected override void OnOpen(Action _onOpenComplete)
    {
        // 注册事件并刷新本次显示数据。
        base.OnOpen(_onOpenComplete);
    }

    protected override void OnClose(Action _onCloseComplete)
    {
        // 移除事件和计时器。
        base.OnClose(_onCloseComplete);
    }
}

业务打开窗口使用:

HFFunCatalogue.UIManager.OpenWindow<ExampleWindow>();

不要直接实例化窗口 Prefab,也不要直接调用内部 HFUIWindowMgr 单例。

isUnloadOnClose 默认是 false。设为 true 后,通过 UI 管理器或窗口自身调用 CloseWindow 时,会在 OnClose 完成后自动销毁缓存实例并卸载窗口资源,适用于低频且内存占用较大的窗口。频繁开关的窗口应保留默认缓存行为,避免反复加载 Prefab 和 AssetBundle。

二、生命周期规则

  • OnRunAwake:实例首次绑定时执行一次,适合控件查找、按钮绑定和虚拟列表初始化。
  • OnOpen:每次打开执行,适合注册事件、应用参数和刷新数据数量。
  • OnClose:每次关闭执行,与 OnOpen 中的监听、计时器和临时状态对称清理。
  • 重写 OnOpen(Action) 或 OnClose(Action) 时必须保证完成回调最终且仅调用一次;自动卸载窗口会在 OnClose 完成回调后执行。
  • 列表初始化不能依赖数据已经到达;先以数量 0 初始化,数据准备后再刷新数量。

三、单列虚拟列表

单列或单行滚动使用 LoopListView2 包装接口:

m_List.SetLoopListViewItemPrefabs(
    new GameObject[] { m_ItemTemplate.gameObject }, 0, 1);

m_List.InitListView(
    0,
    OnGetItem,
    OnCreateItem,
    OnUpdateItem,
    OnGetPrefabIndex,
    false);

数据准备或变化后:

m_List.RefreshDataTotalCount(m_DataList == null ? 0 : m_DataList.Count);

回调职责:

回调 职责
OnGetItem 从已创建节点取得 SFUI_ScrollRectItem
OnCreateItem 首次创建绑定类和 SimulateBehaviour
OnUpdateItem 根据 dataIndex 更新显示,不能假设旧数据仍有效
OnGetPrefabIndex 多模板时返回模板索引,单模板返回 0

垂直列表模板通常使用左上锚点和 pivot (0, 1),宽度与 viewport 匹配;水平列表按滚动方向设置。模板尺寸或 pivot 错误会造成重叠、偏移或不可见。

四、多列虚拟列表

存在固定列数、GridLayoutGroup 语义或二维排布时使用 LoopGridView:

m_Grid.SetLoopGridViewItemPrefabs(
    new GameObject[] { m_ItemTemplate.gameObject },
    new Vector2(216, 293),
    new Vector2(50, 30),
    new RectOffset(55, 55, 0, 0),
    4,
    1);

m_Grid.InitGridView(
    0,
    OnGetGridItem,
    OnCreateGridItem,
    OnUpdateGridItem,
    OnGetGridPrefabIndex);

刷新数量:

m_Grid.RefreshGridDataTotalCount(m_DataList == null ? 0 : m_DataList.Count);

不要通过单列 ListView 模拟多列布局。Grid 的 itemSize、spacing、padding 和列数必须与 Prefab 视觉布局一致。

五、Prefab 设置

  • 模板节点保持 SetActive(false),但必须位于窗口层级中以便引用收集。
  • SFUI_ScrollRect、viewport、content 和模板引用必须完整。
  • 虚拟列表初始化会关闭旧 LayoutGroup 和 ContentSizeFitter,运行时布局由 SuperScrollView 管理。
  • 不要因为这些旧组件在运行时被关闭就重新勾选或依赖它们计算虚拟列表尺寸。
  • 检查模板上的 LoopListViewItem2 或 LoopGridViewItem;包装层会补组件,但 Prefab 结构仍需正确。
  • UIMono 收集需要包含 inactive 子节点,不能只扫描激活节点。

六、验证清单

  1. 构建 SFMono.csproj 和涉及的 GameHF.csproj。
  2. 刷新 Unity Assets 并读取 Console。
  3. 检查 Prefab 的 viewport、content、模板、RectTransform 和组件启用状态。
  4. 验证空数据、1 个元素、超出视口、多次刷新和重复打开。
  5. 滚动后验证元素回收与复用,没有 KeyNotFoundException、旧数据显示或重复创建。
  6. 多列列表验证最后一行不足列数时的尺寸和定位。

文档关联

文档 关联原因
UI 窗口系统 窗口框架入口与生命周期
界面拼接 Prefab 结构和视觉实现
UI 拼接工具 JSON/Prefab 双向转换
开发规则 通用 UI 和 C# 规则