# UIFrame **Repository Path**: Jerry12186/uiframe ## Basic Information - **Project Name**: UIFrame - **Description**: 基于 Unity 的 UI 框架,旨在简化 UI 管理和组件创建,并提供一些常用的 UI 工具和功能 - **Primary Language**: C# - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 11 - **Forks**: 3 - **Created**: 2025-03-07 - **Last Updated**: 2026-02-28 ## Categories & Tags **Categories**: desktop-ui **Tags**: None ## README # Jerry UI Framework 技术文档 ## 1. 框架概述 Jerry UI Framework 是一个基于 Unity 的轻量级 UI 框架,提供了一套完整的 UI 管理、组件系统和工具集,旨在简化 Unity 项目中的 UI 开发流程。 ### 1.1 核心功能 - **窗口管理**:统一的窗口打开、关闭、销毁机制 - **组件系统**:扩展的 UI 组件,支持丰富的交互功能 - **红点系统**:灵活的红点通知系统 - **定时器系统**:便捷的定时任务管理 - **事件系统**:全局事件管理 - **编辑器扩展**:简化 UI 开发的编辑器工具 ### 1.2 目录结构 ``` UIFramework/ ├── Runtime/ # 运行时代码 │ ├── Tools/ # 工具类 │ │ ├── RedPoint/ # 红点系统 │ │ │ ├── EventEnum.cs # 事件枚举定义 │ │ │ ├── RedPointMgr.cs # 红点管理器 │ │ │ ├── RedPointNode.cs # 红点节点 │ │ │ ├── RedPoints.cs # 红点树结构定义 │ │ ├── Timer/ # 定时器系统 │ │ │ ├── Timer.cs # 定时器类 │ │ │ ├── TimerManager.cs # 定时器管理器 │ │ ├── CoroutineTool.cs # 协程工具 │ │ ├── EventMgr.cs # 事件管理器 │ │ ├── NetImageCache.cs # 网络图片缓存 │ ├── UI/ # UI 核心代码 │ │ ├── Core/ # 核心功能 │ │ │ ├── Component/ # UI 组件 │ │ │ │ ├── IComponent.cs # 组件接口 │ │ │ │ ├── JButton.cs # 按钮组件 │ │ │ │ ├── JDropdown.cs # 下拉框组件 │ │ │ │ ├── JDropdownTMP.cs # TextMeshPro 下拉框组件 │ │ │ │ ├── JImage.cs # 图片组件 │ │ │ │ ├── JInput.cs # 输入框组件 │ │ │ │ ├── JInputTMP.cs # TextMeshPro 输入框组件 │ │ │ │ ├── JList.cs # 列表组件 │ │ │ │ ├── JListGrid.cs # 列表项组件 │ │ │ │ ├── JListLoop.cs # 循环列表组件 │ │ │ │ ├── JSlider.cs # 滑块组件 │ │ │ │ ├── JText.cs # 文本组件 │ │ │ │ ├── JTextMesh.cs # TextMeshPro 文本组件 │ │ │ │ ├── JToggle.cs # 开关组件 │ │ │ │ ├── JTree.cs # 树组件 │ │ │ │ ├── JTreeNode.cs # 树节点组件 │ │ │ │ ├── JWindow.cs # 窗口容器组件 │ │ │ ├── JLoader.cs # 资源加载器 │ │ │ ├── JTreeData.cs # 树数据结构 │ │ │ ├── UIBase.cs # 窗口基类 │ │ │ ├── UIConfig.cs # UI 配置 │ │ │ ├── UIManager.cs # UI 管理器 │ │ ├── Shader/ # UI shader │ │ │ ├── SequenceAnimation.shader # 序列动画 shader │ │ │ ├── TextOutline.shader # 文本描边 shader │ │ │ ├── TextShadow.shader # 文本阴影 shader │ │ │ ├── UI_SequenceAnimation.mat # 序列动画材质 │ │ │ ├── UI_TextOutline.mat # 文本描边材质 │ │ │ ├── UI_TextShadow.mat # 文本阴影材质 │ ├── AllPaths.cs # 路径配置 ├── Samples/ # 示例 │ ├── RedPointSample.unitypackage # 红点系统示例 │ ├── Samples.unitypackage # 其他示例 │ ├── UIEditor.unity # UI 编辑器场景 ├── Jerry.UiFrame.asmdef # 程序集定义 ├── LICENSE # 许可证 ├── README.md # 技术文档 ├── package.json # 包配置 ``` **目录说明**: - **Runtime/**:运行时代码,包含框架的核心功能实现 - **Tools/**:工具类,提供各种辅助功能 - **UI/**:UI 核心代码,包含组件系统和窗口管理 - **Samples/**:示例代码和场景,展示框架的使用方法 - **根目录文件**: - `Jerry.UiFrame.asmdef`:Unity 程序集定义文件 - `LICENSE`:项目许可证 - `README.md`:技术文档 - `package.json`:npm 包配置文件(用于 Package Manager) **核心文件说明**: - **UIManager.cs**:UI 管理器,负责窗口的创建、管理和销毁 - **UIBase.cs**:窗口基类,所有 UI 窗口都继承自此类 - **UIConfig.cs**:UI 配置,提供全局配置项 - **IComponent.cs**:组件接口,所有 UI 组件都实现此接口 - **EventMgr.cs**:事件管理器,处理全局事件 - **RedPointMgr.cs**:红点管理器,处理红点系统 - **TimerManager.cs**:定时器管理器,处理定时任务 - **CoroutineTool.cs**:协程工具,管理全局协程 - **NetImageCache.cs**:网络图片缓存,处理网络图片加载和缓存 ## 2. 核心系统 ### 2.1 UI 管理系统 #### UIManager `UIManager` 是整个 UI 框架的核心,负责管理所有 UI 窗口的生命周期。 **主要功能**: - 单例模式管理 - 窗口的打开、关闭、销毁 - 窗口优先级管理 - 窗口缓存管理 - 自动窗口数量控制 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `OpenWindow()` | 打开指定类型的窗口 | `bStay`: 是否常驻
`p`: 窗口参数 | `T`: 窗口实例 | | `CloseWindow()` | 关闭指定类型的窗口 | 无 | 无 | | `DestroyWindow()` | 销毁指定类型的窗口 | 无 | 无 | | `DestroyAllWindow()` | 销毁所有窗口 | 无 | 无 | | `GetWindow()` | 获取已打开的窗口 | 无 | `T`: 窗口实例 | **窗口优先级管理**: - 每次打开窗口时,`UIManager` 会为窗口分配一个优先级值 - 常驻窗口的优先级为 `int.MaxValue`,不会被自动销毁 - 普通窗口的优先级根据打开顺序递增 - 当窗口数量超过 `UIConfig.MaxExistWindows` 时,会自动销毁优先级最低的窗口 **窗口缓存机制**: - 打开窗口时,会先检查窗口是否已经存在 - 如果窗口已存在,则直接激活并置于顶层 - 如果窗口不存在,则创建新实例 - 关闭窗口时,窗口会被隐藏而不是销毁,以便下次快速打开 **使用示例**: ```csharp // 打开普通窗口 var win = UIManager.Instance.OpenWindow(); // 打开常驻窗口(不会被自动销毁) var persistentWin = UIManager.Instance.OpenWindow(true); // 打开带有参数的窗口 var win = UIManager.Instance.OpenWindow(itemId); // 关闭窗口 UIManager.Instance.CloseWindow(); // 获取窗口 var win = UIManager.Instance.GetWindow(); // 销毁所有窗口(慎用) UIManager.Instance.DestroyAllWindow(); ``` #### UIBase `UIBase` 是所有 UI 窗口的基类,提供了窗口的基本功能和生命周期管理。 **主要功能**: - 窗口生命周期管理 - 参数接收和处理 - 子组件获取 - 遮罩点击关闭 - 窗口层级管理 **生命周期方法**: | 方法 | 描述 | 调用时机 | |------|------|----------| | `OnInitData()` | 初始化数据和组件引用 | `Start()` 中调用,仅调用一次 | | `OnOpen()` | 窗口打开时调用 | `OnEnable()` 中调用,每次窗口激活时都会调用 | | `OnReActivate()` | 窗口重新激活时调用 | 当其他窗口关闭,当前窗口重新成为顶层窗口时调用 | **参数传递机制**: - 通过 `OpenWindow` 方法的可变参数传递数据 - 在窗口内部通过 `Param` 数组接收参数 - 可以在 `OnInitData` 或 `OnOpen` 方法中处理这些参数 **组件获取**: - 使用 `GetChild` 方法获取窗口内的子组件 - 支持通过路径获取深层嵌套的组件 - 返回类型为 `IComponent`,可以转换为具体的组件类型 **遮罩点击关闭**: - 设置 `IsClickMaskClose` 为 `true` 启用遮罩点击关闭功能 - 适用于弹窗类窗口,提升用户体验 **使用示例**: ```csharp public class MainWindow : UIBase { private JButton _btnStart; private JText _txtTitle; protected override void OnInitData() { base.OnInitData(); // 获取组件引用 _btnStart = GetChild("BtnStart") as JButton; _txtTitle = GetChild("TxtTitle") as JText; // 添加事件监听 _btnStart.onClick.AddListener(OnStartClick); } protected override void OnOpen() { base.OnOpen(); // 窗口打开时的逻辑 if (Param != null && Param.Length > 0) { // 处理传递的参数 string title = Param[0] as string; if (!string.IsNullOrEmpty(title)) { _txtTitle.text = title; } } } protected override void OnReActivate() { base.OnReActivate(); // 窗口重新激活时的逻辑 Debug.Log("MainWindow reactivated"); } private void OnStartClick() { UIManager.Instance.OpenWindow("Game Start"); } } // 弹窗示例 public class ConfirmDialog : UIBase { private JText _txtMessage; private JButton _btnConfirm; private JButton _btnCancel; public ConfirmDialog() { // 启用遮罩点击关闭 IsClickMaskClose = true; } protected override void OnInitData() { base.OnInitData(); _txtMessage = GetChild("TxtMessage") as JText; _btnConfirm = GetChild("BtnConfirm") as JButton; _btnCancel = GetChild("BtnCancel") as JButton; _btnConfirm.onClick.AddListener(OnConfirmClick); _btnCancel.onClick.AddListener(OnCancelClick); } protected override void OnOpen() { base.OnOpen(); if (Param != null && Param.Length > 0) { string message = Param[0] as string; _txtMessage.text = message; } } private void OnConfirmClick() { // 确认逻辑 CloseWindow(); } private void OnCancelClick() { // 取消逻辑 CloseWindow(); } } ``` #### UIConfig `UIConfig` 提供了 UI 框架的全局配置。 **主要配置项**: | 配置项 | 描述 | 默认值 | |--------|------|--------| | `MaxExistWindows` | 最大存在窗口数 | 10 | | `BtnClickSoundPath` | 按钮点击音效路径 | 无 | | `UiCamera` | UI 渲染相机 | 无 | | `UiMaskColor` | 遮罩颜色 | `new Color(1, 1, 1, 0.4f)` | | `UiAudioVolume` | UI 音效音量 | 无 | ### 2.2 组件系统 #### IComponent `IComponent` 是所有 UI 组件的接口,定义了组件的基本方法。 **方法**: - `SetVisible(bool bVisible)`: 设置组件可见性 - `GetChild(string childName)`: 获取子组件,支持泛型 #### 主要组件 | 组件 | 描述 | 继承自 | 特殊功能 | |------|------|--------|----------| | `JButton` | 按钮组件 | `Button` | 长按、重复点击、点击音效、带参数事件 | | `JImage` | 图片组件 | `Image` | 基础图片功能 | | `JText` | 文本组件 | `Text` | 基础文本功能 | | `JTextMesh` | TextMeshPro 文本组件 | `TMP_Text` | TextMeshPro 支持 | | `JInput` | 输入框组件 | `InputField` | 基础输入功能 | | `JInputTMP` | TextMeshPro 输入框组件 | `TMP_InputField` | TextMeshPro 支持 | | `JDropdown` | 下拉框组件 | `Dropdown` | 基础下拉功能 | | `JDropdownTMP` | TextMeshPro 下拉框组件 | `TMP_Dropdown` | TextMeshPro 支持 | | `JSlider` | 滑块组件 | `Slider` | 基础滑块功能 | | `JToggle` | 开关组件 | `Toggle` | 基础开关功能 | | `JList` | 列表组件 | `ScrollRect` | 滚动列表、多选、单选、自定义渲染 | | `JListGrid` | 列表项组件 | `MonoBehaviour` | 列表项基础功能 | | `JListLoop` | 循环列表组件 | `JList` | 循环滚动列表、性能优化 | | `JTree` | 树组件 | `MonoBehaviour` | 树形结构展示、节点展开/折叠 | | `JTreeNode` | 树节点组件 | `MonoBehaviour` | 树节点基础功能 | | `JWindow` | 窗口容器组件 | `MonoBehaviour` | 窗口基础功能 | #### 组件详细说明 ##### JButton `JButton` 是对 Unity 原生 `Button` 的扩展,提供了更多交互功能。 **特殊功能**: - **长按事件**:当按钮被长按超过指定时间时触发 - **重复点击**:长按期间可以设置重复触发事件的频率 - **点击音效**:支持全局默认音效和单个按钮自定义音效 - **带参数事件**:支持传递 `GameObject` 参数的事件 **属性**: - `holdTime`:长按触发时间阈值(默认 1 秒) - `repeatRate`:长按重复触发频率(默认 0.3 秒) - `clickSound`:自定义点击音效路径 **事件**: - `onClick`:点击事件 - `onLongPress`:长按事件 - `onPressRepeat`:长按重复事件 - `onBtnDown`:按下事件 - `onBtnUp`:释放事件 - `onClickObj`:带参数的点击事件 - `onLongPressObj`:带参数的长按事件 - `onPressRepeatObj`:带参数的长按重复事件 - `onBtnDownObj`:带参数的按下事件 - `onBtnUpObj`:带参数的释放事件 **使用示例**: ```csharp // 获取按钮组件 var btn = GetChild("BtnStart") as JButton; // 添加点击事件 btn.onClick.AddListener(() => { Debug.Log("Button clicked!"); }); // 添加长按事件 btn.onLongPress.AddListener(() => { Debug.Log("Button long pressed!"); }); // 添加按下事件 btn.onBtnDown.AddListener(() => { Debug.Log("Button down!"); }); // 添加释放事件 btn.onBtnUp.AddListener(() => { Debug.Log("Button up!"); }); // 添加带参数的事件 btn.onClickObj.AddListener((go) => { Debug.Log($"Button {go.name} clicked!"); }); ``` ##### JList `JList` 是一个功能强大的列表组件,支持滚动、多选、单选等功能。 **特殊功能**: - **自定义渲染**:通过事件回调自定义列表项的渲染 - **滚动控制**:支持平滑滚动到指定位置 - **选择模式**:支持单选和多选 - **性能优化**:只渲染可视区域内的列表项 **属性**: - `ItemCount`:列表项数量 - `ItemPrefab`:列表项预制体 - `Spacing`:列表项间距 - `Orientation`:列表方向(水平/垂直) **事件**: - `OnItemRender`:列表项渲染事件 - `OnItemClick`:列表项点击事件 - `OnItemSelect`:列表项选择事件 **使用示例**: ```csharp // 获取列表组件 var list = GetChild("ItemList") as JList; // 设置列表项数量 list.ItemCount = 100; // 添加列表项渲染事件 list.OnItemRender += (grid, index) => { // 渲染列表项 var text = grid.GetChild("ItemText"); text.text = $"Item {index}"; var icon = grid.GetChild("ItemIcon"); icon.sprite = Resources.Load($"Icons/item{(index % 10)}"); }; // 添加列表项点击事件 list.OnItemClick += (grid, index) => { Debug.Log($"Item {index} clicked!"); }; // 滚动到指定索引 list.ScrollToIndex(50, true); ``` ##### JListLoop `JListLoop` 是对 `JList` 的扩展,实现了循环滚动列表功能,特别适合需要无限滚动的场景。 **特殊功能**: - **循环滚动**:列表项可以无限循环滚动 - **性能优化**:只创建固定数量的列表项,通过复用实现无限滚动 - **无缝衔接**:滚动到列表末尾时会平滑过渡到列表开头 **使用示例**: ```csharp // 获取循环列表组件 var loopList = GetChild("LoopList") as JListLoop; // 设置列表项数量 loopList.ItemCount = 100; // 添加列表项渲染事件 loopList.OnItemRender += (grid, index) => { var text = grid.GetChild("ItemText"); text.text = $"Loop Item {index}"; }; // 循环列表会自动处理滚动逻辑 ``` ##### JTree `JTree` 是一个树形结构展示组件,支持节点的展开和折叠。 **特殊功能**: - **树形结构**:支持多级嵌套的树形结构 - **节点展开/折叠**:可以点击节点展开或折叠子节点 - **自定义渲染**:支持自定义节点的渲染 **使用示例**: ```csharp // 获取树组件 var tree = GetChild("Tree") as JTree; // 创建树数据 var rootNode = new JTreeData("Root"); var childNode1 = new JTreeData("Child 1"); var childNode2 = new JTreeData("Child 2"); var grandChildNode = new JTreeData("Grand Child"); // 构建树结构 childNode1.AddChild(grandChildNode); rootNode.AddChild(childNode1); rootNode.AddChild(childNode2); // 设置树数据 tree.SetTreeData(rootNode); // 添加节点点击事件 tree.OnNodeClick += (nodeData) => { Debug.Log($"Node clicked: {nodeData.Name}"); }; ``` ### 2.3 红点系统 红点系统用于显示通知信息,如未读消息、新任务等,是游戏和应用中常见的用户提醒机制。 #### 红点系统架构 红点系统采用树形结构管理红点信息,由以下核心组件组成: - **RedPointMgr**:红点管理器,负责红点树的创建和红点数量的管理 - **RedPoints**:红点树结构定义,用于声明所有红点节点 - **RedPointNode**:红点节点,存储单个红点的信息 - **EventEnum**:事件定义,用于红点更新通知 #### RedPointMgr `RedPointMgr` 是红点系统的核心管理器,负责红点树的初始化、节点管理和红点数量更新。 **主要功能**: - **红点树初始化**:根据 `RedPoints` 中定义的节点创建完整的红点树 - **自动节点创建**:支持动态创建红点节点,自动处理父子关系 - **红点数量管理**:设置和获取红点数量,自动更新父节点计数 - **事件通知**:红点数量变化时发送事件通知 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `Init()` | 初始化红点系统 | 无 | 无 | | `CreateNode(string nodeName)` | 创建红点节点 | `nodeName`: 节点名称 | `RedPointNode`: 创建的节点 | | `SetPointCnt(string pointName, int num)` | 设置红点数量 | `pointName`: 节点名称
`num`: 红点数量 | 无 | | `GetRedPointCnt(string pointName)` | 获取红点数量 | `pointName`: 节点名称 | `int`: 红点数量 | **使用示例**: ```csharp // 初始化红点系统 RedPointMgr.Instance.Init(); // 动态添加红点节点 RedPoints.AddNode("Root_Mail"); RedPoints.AddNode("Root_Task_Daily"); RedPoints.AddNode("Root_Task_Weekly"); // 设置红点数量 RedPointMgr.Instance.SetPointCnt("Root_Mail", 5); RedPointMgr.Instance.SetPointCnt("Root_Task_Daily", 3); RedPointMgr.Instance.SetPointCnt("Root_Task_Weekly", 2); // 获取红点数量 int mailCount = RedPointMgr.Instance.GetRedPointCnt("Root_Mail"); int taskCount = RedPointMgr.Instance.GetRedPointCnt("Root_Task"); // 自动汇总子节点数量 int totalCount = RedPointMgr.Instance.GetRedPointCnt("Root"); // 自动汇总所有节点数量 ``` #### RedPoints `RedPoints` 是一个静态类,用于定义红点树的结构。 **主要功能**: - **根节点定义**:定义红点树的根节点 - **节点声明**:声明所有需要的红点节点 - **动态添加**:支持运行时动态添加红点节点 **节点命名规则**: - 节点名称必须以 `Root` 开头 - 父子节点通过下划线 `_` 分隔,如 `Root_Task_Daily` 表示 `Root_Task` 的子节点 **使用示例**: ```csharp // 静态定义红点节点 public static class RedPoints { public const string Root = "Root"; /// /// 只需要填入最终的叶子节点 /// internal static string[] TreeNodes = new string[] { "Root_Mail", "Root_Task_Daily", "Root_Task_Weekly", "Root_Bag", "Root_Activity", }; /// /// 动态添加红点节点 /// /// 节点名称,必须以Root开头 public static void AddNode(string nodeName) { if (!TreeNodes.Contains(nodeName)) { var newList = new List(TreeNodes) {nodeName}; TreeNodes = newList.ToArray(); } } } ``` #### RedPointNode `RedPointNode` 是红点节点类,存储单个红点的信息和父子关系。 **主要属性**: - `name`:节点名称 - `parent`:父节点引用 - `children`:子节点字典 - `redPointCnt`:红点数量 **核心功能**: - 存储红点数量 - 管理父子关系 - 自动向上更新父节点计数 #### 红点更新机制 红点系统使用事件机制通知UI更新: 1. 当设置红点数量时,`RedPointMgr` 会更新对应节点的数量 2. 自动向上遍历更新所有父节点的数量 3. 发送 `EventEnum.RedPointUpdate` 事件通知UI更新 **UI响应示例**: ```csharp public class MainWindow : UIBase { private JImage _mailRedPoint; private JText _mailRedPointText; private JImage _taskRedPoint; private JText _taskRedPointText; protected override void OnInitData() { base.OnInitData(); _mailRedPoint = GetChild("MailRedPoint") as JImage; _mailRedPointText = GetChild("MailRedPointText") as JText; _taskRedPoint = GetChild("TaskRedPoint") as JImage; _taskRedPointText = GetChild("TaskRedPointText") as JText; // 订阅红点更新事件 EventMgr.AddListener(OnRedPointUpdate); // 初始化红点显示 UpdateRedPointDisplay(); } private void OnRedPointUpdate() { UpdateRedPointDisplay(); } private void UpdateRedPointDisplay() { // 更新邮件红点 int mailCount = RedPointMgr.Instance.GetRedPointCnt("Root_Mail"); _mailRedPoint.SetVisible(mailCount > 0); _mailRedPointText.text = mailCount.ToString(); // 更新任务红点 int taskCount = RedPointMgr.Instance.GetRedPointCnt("Root_Task"); _taskRedPoint.SetVisible(taskCount > 0); _taskRedPointText.text = taskCount.ToString(); } private void OnMailClick() { // 打开邮件窗口 UIManager.Instance.OpenWindow(); // 清除邮件红点 RedPointMgr.Instance.SetPointCnt("Root_Mail", 0); } private void OnTaskClick() { // 打开任务窗口 UIManager.Instance.OpenWindow(); // 清除任务红点 RedPointMgr.Instance.SetPointCnt("Root_Task_Daily", 0); RedPointMgr.Instance.SetPointCnt("Root_Task_Weekly", 0); } } ``` #### 红点系统最佳实践 1. **合理设计红点树结构**:根据功能模块划分红点节点,保持树形结构清晰 2. **只设置叶子节点数量**:红点系统会自动计算父节点数量,不要直接设置非叶子节点的数量 3. **及时清除红点**:当用户查看相关内容后,及时清除对应的红点 4. **使用事件监听**:通过订阅 `EventEnum.RedPointUpdate` 事件,实现UI的自动更新 5. **动态添加节点**:对于动态功能,可以使用 `RedPoints.AddNode` 方法动态添加红点节点 **完整使用流程**: 1. 在 `RedPoints` 中定义红点节点 2. 初始化红点系统:`RedPointMgr.Instance.Init()` 3. 在UI中订阅红点更新事件 4. 根据业务逻辑设置红点数量 5. 在UI响应事件中更新红点显示 6. 当用户交互时清除对应红点 ### 2.4 工具系统 工具系统提供了一系列实用的工具类,用于简化开发过程中的常见任务。 #### EventMgr `EventMgr` 是全局事件管理器,用于事件的分发和订阅,实现了组件间的解耦。 **主要功能**: - **泛型事件**:支持任意类型的事件参数 - **事件订阅**:添加和移除事件监听器 - **事件广播**:向所有监听器发送事件 - **线程安全**:支持多线程环境下的事件操作 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `AddListener(Action handler)` | 添加事件监听器 | `handler`: 事件处理方法 | 无 | | `RemoveListener(Action handler)` | 移除事件监听器 | `handler`: 事件处理方法 | 无 | | `Broadcast(T args)` | 广播事件 | `args`: 事件参数 | 无 | | `HasListener()` | 检查是否有监听器 | 无 | `bool`: 是否有监听器 | **使用示例**: ```csharp // 定义事件参数类 public class PlayerLevelUpEvent { public int Level; public string PlayerName; public PlayerLevelUpEvent(int level, string playerName) { Level = level; PlayerName = playerName; } } // 添加事件监听器 EventMgr.AddListener(OnPlayerLevelUp); // 广播事件 EventMgr.Broadcast(new PlayerLevelUpEvent(10, "Player1")); // 移除事件监听器 EventMgr.RemoveListener(OnPlayerLevelUp); // 事件处理方法 private void OnPlayerLevelUp(PlayerLevelUpEvent e) { Debug.Log($"Player {e.PlayerName} level up to {e.Level}!"); // 处理升级逻辑 } // 使用字符串事件(红点系统示例) EventMgr.AddListener(EventEnum.RedPointUpdate, OnRedPointUpdate); EventMgr.Broadcast(EventEnum.RedPointUpdate); EventMgr.RemoveListener(EventEnum.RedPointUpdate, OnRedPointUpdate); ``` #### TimerManager `TimerManager` 是定时器管理器,用于管理定时任务,支持一次性和循环定时器。 **主要功能**: - **精确计时**:基于 Unity 的时间系统,提供精确的定时功能 - **循环定时器**:支持无限循环的定时任务 - **定时器管理**:添加、移除和暂停定时器 - **性能优化**:内部使用高效的数据结构管理定时器 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `AddTimer(float duration, Action callback)` | 添加一次性定时器 | `duration`: 持续时间
`callback`: 回调方法 | `Timer`: 定时器实例 | | `AddTimer(float duration, Action callback, bool loop)` | 添加定时器 | `duration`: 持续时间
`callback`: 回调方法
`loop`: 是否循环 | `Timer`: 定时器实例 | | `RemoveTimer(Timer timer)` | 移除定时器 | `timer`: 定时器实例 | 无 | | `RemoveAllTimers()` | 移除所有定时器 | 无 | 无 | **Timer 类属性**: | 属性 | 描述 | 类型 | |------|------|------| | `duration` | 定时器持续时间 | `float` | | `isLoop` | 是否循环 | `bool` | | `isRunning` | 是否运行中 | `bool` | | `elapsedTime` | 已运行时间 | `float` | **使用示例**: ```csharp // 添加一次性定时器 Timer timer = TimerManager.Instance.AddTimer(1.0f, () => { Debug.Log("Timer triggered!"); }); // 添加循环定时器 Timer loopTimer = TimerManager.Instance.AddTimer(0.5f, () => { Debug.Log("Loop timer triggered!"); }, true); // 移除定时器 TimerManager.Instance.RemoveTimer(timer); // 移除所有定时器(慎用) TimerManager.Instance.RemoveAllTimers(); // 使用定时器实例控制 if (loopTimer != null && loopTimer.isRunning) { // 定时器正在运行 } ``` #### CoroutineTool `CoroutineTool` 是协程工具,用于管理协程,提供了一个全局的协程运行环境。 **主要功能**: - **全局协程**:不依赖具体 MonoBehaviour 的协程管理 - **协程控制**:启动、停止协程 - **生命周期管理**:确保协程在适当的时候被清理 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `StartCoroutine(IEnumerator routine)` | 启动协程 | `routine`: 协程方法 | `Coroutine`: 协程实例 | | `StopCoroutine(IEnumerator routine)` | 停止协程 | `routine`: 协程方法 | 无 | | `StopCoroutine(Coroutine coroutine)` | 停止协程 | `coroutine`: 协程实例 | 无 | | `StopAllCoroutines()` | 停止所有协程 | 无 | 无 | **使用示例**: ```csharp // 启动协程 CoroutineTool.Instance.StartCoroutine(MyCoroutine()); // 协程方法 private IEnumerator MyCoroutine() { Debug.Log("Coroutine started!"); yield return new WaitForSeconds(1.0f); Debug.Log("Coroutine executed after 1 second!"); yield return new WaitForSeconds(2.0f); Debug.Log("Coroutine finished after 3 seconds!"); } // 带参数的协程 private IEnumerator CountdownCoroutine(int seconds) { for (int i = seconds; i > 0; i--) { Debug.Log($"Countdown: {i}"); yield return new WaitForSeconds(1.0f); } Debug.Log("Countdown finished!"); } // 启动带参数的协程 CoroutineTool.Instance.StartCoroutine(CountdownCoroutine(5)); ``` #### NetImageCache `NetImageCache` 是网络图片缓存工具,用于加载和缓存网络图片,减少重复下载。 **主要功能**: - **网络图片加载**:从 URL 加载图片 - **图片缓存**:缓存已加载的图片,避免重复下载 - **异步加载**:非阻塞式加载,不影响主线程 - **错误处理**:处理网络错误和图片加载失败的情况 **关键方法**: | 方法 | 描述 | 参数 | 返回值 | |------|------|------|--------| | `LoadImage(string url, Action callback)` | 加载网络图片 | `url`: 图片 URL
`callback`: 加载完成回调 | 无 | | `ClearCache()` | 清除图片缓存 | 无 | 无 | | `GetCacheSize()` | 获取缓存大小 | 无 | `long`: 缓存大小(字节) | **使用示例**: ```csharp // 加载网络图片 NetImageCache.Instance.LoadImage("https://example.com/image.png", (sprite) => { if (sprite != null) { // 图片加载成功 avatarImage.sprite = sprite; } else { // 图片加载失败 Debug.LogError("Failed to load image"); // 使用默认图片 avatarImage.sprite = Resources.Load("DefaultAvatar"); } }); // 清除缓存(在内存不足时使用) NetImageCache.Instance.ClearCache(); // 检查缓存大小 long cacheSize = NetImageCache.Instance.GetCacheSize(); Debug.Log($"Image cache size: {cacheSize / 1024} KB"); ``` ## 3. 编辑器扩展 ### 3.1 CreateUI `CreateUI` 是一个编辑器工具类,用于在 Unity 编辑器中快速创建各种 UI 组件和容器。 **核心功能**: - **创建 UI 容器**:快速创建 2DCanvas 和组件容器 - **创建 UI 组件**:支持创建按钮、图片、列表、滑动条、文本、输入框等多种 UI 组件 - **路径管理**:智能识别资源路径,支持从 Package 或本地 Assets 加载预制体 - **编辑器集成**:在 Unity 编辑器的 GameObject 菜单中添加了创建 UI 组件的菜单项 **主要方法**: | 方法 | 描述 | 参数 | |------|------|------| | `Create2DCanvas()` | 创建 2DCanvas 容器 | 无 | | `CreateComponent()` | 创建 UI 组件 | `parent`: 父物体
`path`: 预制体路径 | | `GetPackageFullPath()` | 获取资源路径 | `out type`: 资源类型 | | `LoadFromPackage()` | 从 Package 中加载资源 | `path`: 资源路径 | **支持创建的组件**: - **容器**:UI容器、组件容器 - **按钮**:普通按钮、TMP按钮 - **图片**:图片组件 - **列表**:常规列表、循环列表、树形结构 - **滑动条**:滑动条组件 - **文本**:普通文本、TextMeshPro文本 - **选择器**:多选、单选 - **输入框**:普通输入框、TextMeshPro输入框 - **下拉框**:普通下拉框、TextMeshPro下拉框 **使用方式**: 在 Unity 编辑器中,通过以下菜单路径创建 UI 组件: `GameObject → 创建UI → [组件类型]` **技术实现**: 1. **路径识别**:通过 `GetPackageFullPath()` 方法识别资源是来自 Package 还是本地 Assets 2. **预制体加载**:根据路径类型加载对应的预制体 3. **组件实例化**:将预制体实例化到指定父物体下 4. **属性设置**:自动设置组件的位置和缩放属性 5. **编辑器集成**:使用 `[MenuItem]` 特性在编辑器菜单中添加创建选项 ### 3.2 GenUIBase `GenUIBase` 是一个编辑器工具,用于生成 UI 窗口的基类代码。 **功能**: - 根据 UI 预制体生成对应的 C# 代码 - 自动生成组件引用 ### 3.3 组件编辑器 框架为部分组件提供了自定义编辑器,如: - `JListEditor` - `JListGridEditor` - `JListLoopEditor` - `JTextEditor` - `JTextMeshEditor` - `JTreeEditor` 这些编辑器提供了更友好的 UI 编辑体验。 ## 4. 最佳实践 ### 4.1 框架设计理念 Jerry UI Framework 采用以下设计理念,理解这些理念有助于更好地使用框架: - **组件化设计**:将 UI 分解为独立的组件,提高代码复用性和可维护性 - **事件驱动**:使用事件系统实现组件间通信,减少耦合 - **性能优先**:内置多种性能优化机制,如窗口缓存、列表复用等 - **易于扩展**:提供清晰的扩展点,方便自定义功能 - **开发者友好**:简化 UI 开发流程,减少重复代码 ### 4.2 窗口管理最佳实践 #### 窗口设计 1. **窗口命名规范**: - 窗口类名应与预制体名称一致 - 使用 PascalCase 命名法,如 `MainWindow` - 预制体放置在统一的目录结构中 2. **窗口分层**: - **主界面层**:游戏的主要界面,如主菜单、游戏界面 - **弹窗层**:临时性窗口,如确认对话框、设置面板 - **提示层**:短暂显示的信息,如通知、成就提示 - **HUD层**:游戏中始终显示的界面元素,如血条、小地图 3. **窗口生命周期管理**: - 使用 `OnInitData` 初始化组件引用和事件监听 - 使用 `OnOpen` 处理每次窗口打开时的逻辑 - 使用 `OnReActivate` 处理窗口重新激活时的逻辑 #### 窗口使用技巧 1. **窗口缓存策略**: - 对于频繁打开的窗口(如背包、技能面板),设置为常驻窗口 - 对于不常用的窗口,使用默认缓存机制 - 根据项目实际情况调整 `UIConfig.MaxExistWindows` 2. **窗口参数传递**: - 对于简单参数,直接通过 `OpenWindow` 方法传递 - 对于复杂参数,创建专门的参数类 - 避免传递过大的对象,考虑使用引用或标识符 3. **窗口关闭方式**: - 使用 `CloseWindow` 方法关闭窗口(会缓存) - 仅在必要时使用 `DestroyWindow` 方法(会销毁) - 对于模态窗口,考虑添加遮罩点击关闭功能 ### 4.3 组件使用最佳实践 #### 通用组件 1. **组件命名规范**: - 使用清晰、描述性的名称,如 `BtnStart`、`TxtScore` - 保持命名风格一致,推荐使用 PascalCase - 对于同类组件,使用统一的命名前缀 2. **事件处理**: - 使用组件提供的事件系统,如 `JButton.onClick` - 避免在 `Update` 中轮询组件状态 - 及时移除不需要的事件监听器,避免内存泄漏 3. **TextMeshPro 优先**: - 优先使用带有 TMP 后缀的组件,如 `JTextMesh`、`JInputTMP` - TextMeshPro 提供更好的文本渲染效果和更多功能 #### 列表组件 1. **列表优化**: - 对于长列表(超过 50 项),使用 `JListLoop` 以获得更好的性能 - 合理设置列表项的预制体复杂度,避免过于复杂 - 使用对象池技术进一步优化列表性能 2. **列表渲染**: - 在 `OnItemRender` 事件中只处理必要的渲染逻辑 - 避免在渲染回调中进行复杂计算或网络请求 - 对于图片资源,考虑使用异步加载 3. **列表交互**: - 对于需要多选的场景,使用列表的多选模式 - 对于复杂的列表项交互,考虑在列表项内部处理 #### 按钮组件 1. **按钮事件**: - 合理使用 `onClick`、`onLongPress` 等事件 - 对于需要长按效果的按钮,调整 `holdTime` 参数 - 对于需要连续触发的按钮,使用 `onPressRepeat` 事件 2. **按钮音效**: - 设置全局默认按钮音效,提升用户体验 - 对于特殊按钮,使用自定义音效 ### 4.4 红点系统最佳实践 1. **红点树设计**: - 按照功能模块组织红点树结构 - 使用有意义的节点名称,如 `Root_Mail`、`Root_Task_Daily` - 保持红点树结构清晰,避免过深的嵌套 2. **红点更新策略**: - 只在数据变化时更新红点数量 - 批量更新红点,避免频繁触发事件 - 使用异步方式处理大量红点的更新 3. **红点显示**: - 根据红点数量显示不同的视觉效果 - 对于数量过多的红点,考虑显示 "99+" 等形式 - 确保红点显示与实际数据一致 ### 4.5 性能优化指南 #### 内存优化 1. **窗口缓存管理**: - 合理设置 `UIConfig.MaxExistWindows`,平衡内存使用和加载速度 - 对于大型游戏,考虑实现窗口预加载机制 - 监控窗口内存使用,及时清理不需要的窗口 2. **资源管理**: - 使用 `NetImageCache` 缓存网络图片,减少重复下载 - 合理设置图片缓存大小,避免内存占用过高 - 对于大型资源,使用异步加载和卸载 3. **对象池**: - 对于频繁创建和销毁的对象,使用对象池技术 - 列表组件内部已实现对象池,无需额外处理 #### CPU 优化 1. **列表优化**: - 使用 `JListLoop` 处理长列表,减少 Draw Call - 合理设置列表的可见区域和缓存数量 - 避免在列表渲染回调中进行复杂计算 2. **事件处理**: - 减少事件监听器的数量,合并相似的事件处理 - 使用事件委托而非字符串事件,提高性能 - 及时移除不需要的事件监听器 3. **协程管理**: - 使用 `CoroutineTool` 统一管理协程,避免协程泄漏 - 对于长时间运行的协程,考虑添加终止机制 - 避免在协程中使用过多的 `WaitForSeconds`,考虑使用定时器 #### 渲染优化 1. **UI 层级**: - 合理组织 UI 层级,减少过度绘制 - 使用 `Canvas` 分组,将静态和动态 UI 分离 - 避免在 UI 中使用复杂的 shader 2. **文本渲染**: - 使用 TextMeshPro 提升文本渲染质量和性能 - 合理设置文本的字体大小和样式 - 对于静态文本,考虑使用图集 3. **图片优化**: - 使用适当大小的图片,避免过度缩放 - 合理使用图片压缩格式 - 对于重复使用的图片,确保只加载一次 ### 4.6 代码组织最佳实践 1. **代码结构**: - 按照功能模块组织代码 - 使用命名空间避免冲突 - 保持代码文件的合理大小,避免过大的文件 2. **代码风格**: - 遵循 C# 代码风格规范 - 使用清晰的命名和注释 - 保持代码缩进一致 3. **错误处理**: - 添加适当的错误检查和异常处理 - 对于网络请求等异步操作,处理失败情况 - 提供有意义的错误信息 4. **测试策略**: - 编写单元测试验证核心功能 - 进行性能测试,确保在目标设备上运行流畅 - 进行用户体验测试,确保界面响应及时 ## 5. 示例 ### 5.1 基础窗口示例 ```csharp public class LoginWindow : UIBase { private JInput _inputUsername; private JInput _inputPassword; private JButton _btnLogin; private JButton _btnRegister; protected override void OnInitData() { base.OnInitData(); _inputUsername = GetChild("InputUsername") as JInput; _inputPassword = GetChild("InputPassword") as JInput; _btnLogin = GetChild("BtnLogin") as JButton; _btnRegister = GetChild("BtnRegister") as JButton; _btnLogin.onClick.AddListener(OnLoginClick); _btnRegister.onClick.AddListener(OnRegisterClick); } private void OnLoginClick() { string username = _inputUsername.text; string password = _inputPassword.text; // 登录逻辑 if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { // 登录成功,打开主界面 UIManager.Instance.CloseWindow(); UIManager.Instance.OpenWindow(); } } private void OnRegisterClick() { // 打开注册窗口 UIManager.Instance.OpenWindow(); } } ``` ### 5.2 列表示例 ```csharp public class InventoryWindow : UIBase { private JList _itemList; private List _items; protected override void OnInitData() { base.OnInitData(); _itemList = GetChild("ItemList") as JList; _items = GetItems(); // 获取物品数据 // 设置列表项数量 _itemList.ItemCount = _items.Count; // 添加列表项渲染事件 _itemList.OnItemRender += OnItemRender; // 添加列表项点击事件 _itemList.OnItemClick += OnItemClick; } private void OnItemRender(JListGrid grid, int index) { if (index < 0 || index >= _items.Count) return; Item item = _items[index]; // 设置物品图标 JImage icon = grid.GetChild("ItemIcon"); icon.sprite = Resources.Load(item.IconPath); // 设置物品名称 JText name = grid.GetChild("ItemName"); name.text = item.Name; // 设置物品数量 JText count = grid.GetChild("ItemCount"); count.text = item.Count.ToString(); } private void OnItemClick(JListGrid grid, int index) { if (index < 0 || index >= _items.Count) return; Item item = _items[index]; Debug.Log($"Clicked item: {item.Name}"); // 打开物品详情窗口 UIManager.Instance.OpenWindow(item); } private List GetItems() { // 模拟获取物品数据 List items = new List(); for (int i = 0; i < 50; i++) { items.Add(new Item { Name = "Item " + i, IconPath = "Icons/item" + (i % 10), Count = Random.Range(1, 100) }); } return items; } } ``` ### 5.3 红点系统示例 ```csharp public class MainWindow : UIBase { private JImage _mailRedPoint; private JText _mailRedPointText; private JImage _taskRedPoint; private JText _taskRedPointText; protected override void OnInitData() { base.OnInitData(); _mailRedPoint = GetChild("MailRedPoint") as JImage; _mailRedPointText = GetChild("MailRedPointText") as JText; _taskRedPoint = GetChild("TaskRedPoint") as JImage; _taskRedPointText = GetChild("TaskRedPointText") as JText; // 订阅红点更新事件 EventMgr.AddListener(OnRedPointUpdate); // 初始化红点显示 UpdateRedPointDisplay(); } private void OnRedPointUpdate() { UpdateRedPointDisplay(); } private void UpdateRedPointDisplay() { // 更新邮件红点 int mailCount = RedPointMgr.Instance.GetRedPointCnt("Root_Mail"); _mailRedPoint.SetVisible(mailCount > 0); _mailRedPointText.text = mailCount.ToString(); // 更新任务红点 int taskCount = RedPointMgr.Instance.GetRedPointCnt("Root_Task"); _taskRedPoint.SetVisible(taskCount > 0); _taskRedPointText.text = taskCount.ToString(); } private void OnMailClick() { // 打开邮件窗口 UIManager.Instance.OpenWindow(); // 清除邮件红点 RedPointMgr.Instance.SetPointCnt("Root_Mail", 0); } private void OnTaskClick() { // 打开任务窗口 UIManager.Instance.OpenWindow(); // 清除任务红点 RedPointMgr.Instance.SetPointCnt("Root_Task", 0); } } ``` ## 6. 常见问题解答 ### 6.1 窗口管理问题 **Q: 窗口无法打开,报错 "Could not load prefab"** A: 检查窗口预制体是否存在于正确的路径下,确保 `AllPaths.cs` 中的路径配置正确。 **Q: 窗口打开后没有显示** A: 检查窗口的 `Canvas` 组件是否正确设置,确保窗口的 `GameObject` 处于激活状态。 **Q: 窗口关闭后再次打开时数据没有更新** A: 确保在 `OnOpen` 方法中处理窗口打开时的数据更新逻辑,而不是只在 `OnInitData` 中处理。 ### 6.2 组件使用问题 **Q: JButton 的长按事件不触发** A: 检查 `holdTime` 属性是否设置合理,确保按钮的 `interactable` 属性为 `true`。 **Q: JList 无法显示数据** A: 确保设置了正确的 `ItemCount`,并添加了 `OnItemRender` 事件监听器。 **Q: 组件事件不触发** A: 检查组件是否正确获取,确保事件监听器在 `OnInitData` 中正确添加。 ### 6.3 红点系统问题 **Q: 红点数量不更新** A: 检查是否调用了 `RedPointMgr.Instance.Init()` 初始化红点系统,确保红点节点名称格式正确。 **Q: 红点更新后 UI 没有刷新** A: 确保订阅了 `EventEnum.RedPointUpdate` 事件,并在事件回调中更新 UI。 **Q: 无法设置红点数量** A: 确保只对叶子节点设置红点数量,非叶子节点的数量会自动计算。 ### 6.4 工具系统问题 **Q: 事件监听器不执行** A: 检查事件类型是否匹配,确保事件监听器在事件广播前添加。 **Q: 定时器不触发** A: 检查 `TimerManager` 是否正确初始化,确保定时器的持续时间设置合理。 **Q: 协程不执行** A: 确保使用 `CoroutineTool.Instance.StartCoroutine` 启动协程,检查协程方法是否正确实现。 **Q: 网络图片加载失败** A: 检查网络连接是否正常,确保图片 URL 正确,考虑添加错误处理逻辑。 ### 6.5 性能问题 **Q: 列表滚动卡顿** A: 对于长列表,使用 `JListLoop` 替代 `JList`,减少列表项的复杂度,避免在渲染回调中进行复杂计算。 **Q: 内存占用过高** A: 调整 `UIConfig.MaxExistWindows` 减少窗口缓存数量,使用 `NetImageCache.ClearCache()` 清理图片缓存。 **Q: 启动时间过长** A: 减少初始加载的窗口数量,使用异步加载资源,考虑实现窗口预加载机制。 ### 6.6 其他问题 **Q: 如何自定义组件** A: 继承自现有的组件类或实现 `IComponent` 接口,添加自定义功能。 **Q: 如何扩展窗口功能** A: 继承自 `UIBase` 类,重写相应的生命周期方法,添加自定义逻辑。 **Q: 如何集成第三方插件** A: 在 `UIConfig.cs` 中添加相应的配置项,在需要的地方调用第三方插件的 API。 **Q: 如何处理多语言支持** A: 可以在 `JText` 和 `JTextMesh` 组件上添加多语言支持,使用 Unity 的 `Localization` 系统或自定义多语言解决方案。 ## 7. 基础窗口示例 框架提供了一系列基础窗口示例,位于 `Scripts/UI/Base` 目录下,展示了各种 UI 组件的使用方法和最佳实践。这些示例窗口可以作为开发者创建自定义窗口的参考。 ### 7.1 示例窗口列表 | 窗口类名 | 功能描述 | 主要组件 | |---------|---------|----------| | `BaseUIButtonWin` | 按钮组件示例 | JButton | | `BaseUIImageWin` | 图片组件示例 | JImage | | `BaseUIListWin` | 列表组件示例 | JList | | `BaseUILoopListWin` | 循环列表组件示例 | JListLoop | | `BaseUIPop1Win` | 弹窗样式1示例 | 基础UI组件 | | `BaseUIPop2Win` | 弹窗样式2示例 | 基础UI组件 | | `BaseUIRedPointWin` | 红点系统示例 | JButton, RedPoint | | `BaseUITestWin` | 测试窗口示例 | 多种UI组件 | | `BaseUITextWin` | 文本组件示例 | JText, JTextMesh | | `BaseUITreeWin` | 树结构组件示例 | JTree | | `BaseUIWinLogicWin` | 窗口逻辑示例 | 多种UI组件 | ### 7.2 示例窗口结构 所有示例窗口都继承自 `UIBase` 类,并在 `Awake` 方法中获取所需的 UI 组件引用。以下是典型的示例窗口结构: ```csharp using Jerry.UiFrame; using UnityEngine; namespace UI.Base { public class BaseUIExampleWin : UIBase { // 组件引用 protected JButton Btn_Example; protected JText Text_Example; protected override void Awake() { // 获取组件引用 Btn_Example = transform.Find("Btn_Example").GetComponent(); Text_Example = transform.Find("Text_Example").GetComponent(); base.Awake(); } // 可以在 OnInitData 中添加事件监听 protected override void OnInitData() { base.OnInitData(); // 添加事件监听 Btn_Example.onClick.AddListener(OnExampleButtonClick); } // 事件处理方法 private void OnExampleButtonClick() { Text_Example.text = "Button clicked!"; } } } ``` ### 7.3 如何使用示例窗口 1. **查看示例代码**:参考示例窗口的实现方式,了解如何正确使用各种 UI 组件 2. **复制修改**:基于示例窗口创建自定义窗口,修改组件引用和业务逻辑 3. **测试功能**:使用示例窗口测试框架的各种功能 4. **学习最佳实践**:从示例中学习 UI 开发的最佳实践 ### 7.4 示例窗口详细说明 #### BaseUIButtonWin `BaseUIButtonWin` 展示了 `JButton` 组件的各种用法,包括普通按钮、TMP按钮、自定义音效按钮、长按按钮和重复点击按钮。 #### BaseUIListWin `BaseUIListWin` 展示了 `JList` 组件的用法,包括单选列表和多选列表的实现。 #### BaseUILoopListWin `BaseUILoopListWin` 展示了 `JListLoop` 组件的用法,包括垂直循环列表和水平循环列表的实现。 #### BaseUIRedPointWin `BaseUIRedPointWin` 展示了红点系统的用法,包括不同层级红点的管理和显示。 #### BaseUITreeWin `BaseUITreeWin` 展示了 `JTree` 组件的用法,包括树形结构的创建和管理。 ## 8. 总结 Jerry UI Framework 是一个功能完整、易于使用的 Unity UI 框架,提供了以下核心优势: 1. **简化 UI 开发**:通过封装常用功能,减少重复代码 2. **灵活的组件系统**:提供丰富的 UI 组件,满足各种需求 3. **高效的窗口管理**:智能的窗口缓存和优先级管理 4. **强大的工具系统**:红点、定时器、事件等工具,简化开发 5. **编辑器扩展**:提供便捷的编辑器工具,提高开发效率 6. **性能优化**:内置多种性能优化机制,确保流畅的用户体验 7. **易于扩展**:清晰的架构设计,方便自定义和扩展 8. **丰富的示例**:提供多种基础窗口示例,帮助开发者快速上手 该框架适用于各种 Unity 项目,特别是需要复杂 UI 交互的游戏和应用。通过合理使用框架提供的功能,可以大大提高 UI 开发的效率和质量,让开发者能够更专注于游戏逻辑和用户体验的设计。 ### 8.1 未来发展 Jerry UI Framework 将持续演进,未来计划添加以下功能: - **UI 动画系统**:提供更丰富的 UI 动画效果 - **数据绑定**:实现 UI 与数据的自动绑定 - **多平台适配**:优化不同平台的 UI 表现 - **更多组件**:扩展组件库,提供更多专业组件 - **可视化编辑器**:提供更直观的 UI 配置工具 我们欢迎社区贡献和反馈,共同打造更好的 Unity UI 开发体验。