From 094ae2eedbde9d737394a318e9de882fd128956d Mon Sep 17 00:00:00 2001 From: Shiliang Date: Fri, 22 May 2026 14:40:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0UI=E7=9B=B8=E5=85=B3=E6=8C=87?= =?UTF-8?q?=E5=8D=97=E6=96=87=E6=A1=A3=20v1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改内容: 1. UI事件绑定指南:新增事件绑定位置规范,强调防重复注册保护,新增SFUI_ScrollRect绑定模式 2. UI窗口开发指南:更新窗口创建流程示例,添加子组件开发示例 3. UI组件使用指南:添加防重复注册保护,新增列表绑定模式和子组件开发规范 --- 03_核心功能/UI事件绑定指南.md | 225 ++++++++++++++++++---------------- 03_核心功能/UI窗口开发指南.md | 200 ++++++++++++++++++++++++------ 03_核心功能/UI组件使用指南.md | 188 ++++++++++++++++++++-------- 3 files changed, 416 insertions(+), 197 deletions(-) diff --git a/03_核心功能/UI事件绑定指南.md b/03_核心功能/UI事件绑定指南.md index 7e2791a..c1baea4 100644 --- a/03_核心功能/UI事件绑定指南.md +++ b/03_核心功能/UI事件绑定指南.md @@ -1,90 +1,146 @@ # StrayFog 框架 UI事件绑定指南 -## 一、事件绑定方式 +## 一、事件绑定位置规范 -### 1.1 代码绑定(使用框架封装组件) +### 1.1 事件绑定位置总览 + +| 事件类型 | 推荐位置 | 原因 | +|---------|---------|------| +| **UI按钮事件** | `OnRunAwake` | 静态稳定,只需注册一次 | +| **列表拖拽事件** | `OnRunAwake` | 窗口生命周期内持续响应 | +| **全局CTC事件** | `OnOpen`/`OnClose` | 窗口关闭后不应响应 | +| **数据刷新逻辑** | `OnOpen` | 每次打开窗口需刷新 | + +### 1.2 推荐绑定模式(防重复注册保护) ```csharp -public class LoginWindow : AbsHFUIWindow +public class BagWindow : AbsHFUIWindow { - private SFUI_Button BtnLogin; - + private SFUI_Button BtnClose; + private SFUI_ScrollRect BagListView; + protected override void OnRunAwake() { base.OnRunAwake(); - // OnRunAwake: 初始化组件引用和按钮事件绑定(仅调用一次) - BtnLogin = FindUICtrlByName("BtnLogin"); - - if (BtnLogin != null) - { - BtnLogin.OnAddClickListener += OnBtnLoginClick; - BtnLogin.OnAddEnterListener += OnBtnLoginEnter; - BtnLogin.OnAddExitListener += OnBtnLoginExit; - } + BtnClose = FindUICtrlByName("BtnClose"); + BagListView = FindUICtrlByName("BagListView"); + + // ✅ 按钮事件放在 OnRunAwake(先取消再注册防止重复) + BtnClose.OnAddClickListener -= OnBtnClose; + BtnClose.OnAddClickListener += OnBtnClose; + + // ✅ 列表事件也放在 OnRunAwake + BagListView.OnAddBeginDragListener -= OnBeginDrag; + BagListView.OnAddBeginDragListener += OnBeginDrag; } protected override void OnOpen(Action _onOpenComplete) { base.OnOpen(_onOpenComplete); - // OnOpen: 注册CTC事件监听(每次打开调用) - HFFunCatalogue.Event.CTCEvent.AddListener( - enHFCTCEvent.LoginResult, OnLoginResult); + // ✅ 全局CTC事件放在 OnOpen + HFFunCatalogue.Event.TCEvent.AddListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); } protected override void OnClose(Action _onCloseComplete) { - // OnClose: 移除事件监听(每次关闭调用) - if (BtnLogin != null) - { - BtnLogin.OnAddClickListener -= OnBtnLoginClick; - BtnLogin.OnAddEnterListener -= OnBtnLoginEnter; - BtnLogin.OnAddExitListener -= OnBtnLoginExit; - } - - HFFunCatalogue.Event.CTCEvent.RemoveListener( - enHFCTCEvent.LoginResult, OnLoginResult); - + // ✅ 全局CTC事件在 OnClose 取消 + HFFunCatalogue.Event.TCEvent.RemoveListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); base.OnClose(_onCloseComplete); } - - void OnBtnLoginClick(GameObject go) - { - // 处理登录逻辑 - 使用框架标准调用方式 - HFEvent_CTC_Login evt = HFFunCatalogue.New(); - evt.account = "player_account"; - evt.password = "encrypted_password"; - evt.Dispatch(); - } - void OnBtnLoginEnter(GameObject go) - { - // 鼠标进入 - } - - void OnBtnLoginExit(GameObject go) - { - // 鼠标退出 - } - - void OnLoginResult(HFEventArgBase arg) - { - // 处理登录结果 - } + private void OnBtnClose(GameObject go) { /* 按钮点击处理 */ } + private void OnBeginDrag(GameObject go) { /* 拖拽处理 */ } + private void OnBagItemUpdate(IHFEventArg arg) { /* 数据更新处理 */ } } ``` -### 1.2 框架封装UI组件列表 +### 1.3 防重复注册原理 -| 组件名 | 基类 | 说明 | -|-------|------|------| -| `SFUI_Button` | `AbsSFUI_Button : Button` | 按钮组件 | -| `SFUI_Text` | `AbsSFUI_Text : Text` | 文本组件 | -| `SFUI_Image` | `AbsSFUI_Image : Image` | 图片组件 | -| `SFUI_Toggle` | `AbsSFUI_Toggle : Toggle` | 开关组件 | -| `SFUI_ScrollRect` | `AbsSFUI_ScrollRect : ScrollRect` | 滚动区域 | -| `SFUI_InputField` | `AbsSFUI_InputField : InputField` | 输入框 | +```csharp +// 先取消注册,再添加注册 +BtnLogin.OnAddClickListener -= OnBtnLoginClick; // 防止重复注册 +BtnLogin.OnAddClickListener += OnBtnLoginClick; // 安全注册 +``` -### 1.3 SFUI_Button事件委托 +**作用**:即使`OnRunAwake`被多次调用(如对象池复用),也不会导致事件重复触发。 + +--- + +## 二、SFUI_ScrollRect 绑定模式 + +### 2.1 列表绑定(推荐) + +```csharp +protected override void OnRunAwake() +{ + base.OnRunAwake(); + + var itemPrefab = FindUICtrlByName("Item"); + BagListView = FindUICtrlByName("BagListView"); + + BagListView.BindEvent( + () => { return m_items.Count; }, // 数据源数量 + (idx) => { return itemPrefab.gameObject; }, // 实例化模板 + OnUpdateItem, // 更新回调 + (item) => { // 组件获取 + return item.gameObject.HFGetOrAddSimulateBehaviour(); + } + ); +} + +private void OnUpdateItem(SFUI_ScrollRectItem item) +{ + var bagItem = item.simulateBehaviour as BagItemSlot; + bagItem.SetData(m_items[item.dataIdx], m_select, item.dataIdx, OnSelect); +} +``` + +### 2.2 列表刷新 + +```csharp +// 刷新整个列表 +BagListView.ShowView(); + +// 刷新指定项 +BagListView.RefreshView(null); + +// 滚动到指定位置 +BagListView.ScrollToIndex(0); +``` + +--- + +## 三、事件系统分类 + +### 3.1 TCEvent(客户端事件) + +```csharp +// 注册监听(在 OnOpen 中) +HFFunCatalogue.Event.TCEvent.AddListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); + +// 取消监听(在 OnClose 中) +HFFunCatalogue.Event.TCEvent.RemoveListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); +``` + +### 3.2 STCEvent(服务器事件) + +```csharp +// 在 Manager 层注册(UI窗口不直接监听) +HFFunCatalogue.Event.STCEvent.AddListener( + enHFSTCEvent.GetBag, OnPushBagItem); +``` + +> **注意**:UI窗口中不直接监听STCEvent,应在Logic层Manager中监听,然后通过TCEvent通知UI。 + +--- + +## 四、按钮事件委托 + +### 4.1 SFUI_Button 事件列表 | 事件 | 委托签名 | 说明 | |-----|---------|------| @@ -92,51 +148,10 @@ public class LoginWindow : AbsHFUIWindow | `OnAddEnterListener` | `Enter(GameObject go)` | 鼠标进入 | | `OnAddExitListener` | `Exit(GameObject go)` | 鼠标退出 | | `OnAddPointerUpListener` | `PointerUp(GameObject go)` | 指针抬起 | +| `OnAddBeginDragListener` | `BeginDrag(GameObject go)` | 开始拖拽 | +| `OnAddEndDragListener` | `EndDrag(GameObject go)` | 结束拖拽 | --- -## 二、事件系统分类 - -### 2.1 CTCEvent(客户端内部事件) - -```csharp -// 注册监听 -HFFunCatalogue.Event.CTCEvent.AddListener( - enHFCTCEvent.CardUpdate, OnCardUpdate); - -// 触发事件 -HFEvent_CTC_CardUpdate evt = HFFunCatalogue.New(); -evt.cardId = 123; -HFFunCatalogue.Event.CTCEvent.Dispatch(evt); -``` - -### 2.2 STCEvent(服务器事件) - -```csharp -// 在Manager中注册 -HFFunCatalogue.Event.STCEvent.AddListener( - enHFSTCEvent.PlayerInfo, OnPlayerInfo); -``` - -> **注意**:UI窗口中不监听STCEvent,应在Logic层Manager中监听。 - ---- - -## 三、事件参数回收 - -### 3.1 使用对象池 - -```csharp -// 获取事件参数(从对象池获取) -HFEvent_CTC_PlayerLevelUp evt = HFFunCatalogue.New(); -evt.playerId = 1; -evt.newLevel = 10; - -// 触发事件(事件系统会自动回收) -HFFunCatalogue.Event.CTCEvent.Dispatch(evt); -``` - ---- - -**版本**: v1.0 +**版本**: v1.1 **适用范围**: GiftGameX项目业务开发人员 \ No newline at end of file diff --git a/03_核心功能/UI窗口开发指南.md b/03_核心功能/UI窗口开发指南.md index 350a928..5bdecbc 100644 --- a/03_核心功能/UI窗口开发指南.md +++ b/03_核心功能/UI窗口开发指南.md @@ -5,52 +5,67 @@ ### 1.1 窗口创建流程 ```csharp -[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/Login/LoginWindow.prefab")] -[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Dynamic, isIgnoreCloseByESC = false)] -public class LoginWindow : AbsHFUIWindow +[HFAssetBundlePath(enHFLanguageRootFolder.ASSETS_GAME_GAMEHFRESOURCE, "UIWindows/Bag/BagWindow.prefab")] +[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Fixed, isIgnoreCloseByESC = false)] +public class BagWindow : AbsHFUIWindow { // ========== UI组件 ========== - private SFUI_Button BtnLogin; - private SFUI_InputField InputName; - private SFUI_InputField InputPassword; - private SFUI_TextMeshProUGUI TxtTitle; + private SFUI_Button BtnClose; + private SFUI_ScrollRect BagListView; + private BagTips tips; + + // ========== 数据 ========== + private List m_items = new List(); + private ItemInfo m_select; protected override void OnRunAwake() { base.OnRunAwake(); - // 初始化UI组件引用(变量名与组件名保持一致) - BtnLogin = FindUICtrlByName("BtnLogin"); - InputName = FindUICtrlByName("InputName"); - InputPassword = FindUICtrlByName("InputPassword"); - TxtTitle = FindUICtrlByName("TxtTitle"); + // ✅ 初始化UI组件引用 + BtnClose = FindUICtrlByName("BtnClose"); + BagListView = FindUICtrlByName("BagListView"); + tips = BindUI("BagTips"); - // 绑定按钮事件 - if (BtnLogin != null) - { - BtnLogin.OnAddClickListener += OnBtnLoginClick; - } + // ✅ 按钮事件绑定(带防重复注册保护) + BtnClose.OnAddClickListener -= OnBtnClose; + BtnClose.OnAddClickListener += OnBtnClose; + + // ✅ 列表绑定 + var itemPrefab = FindUICtrlByName("Item"); + BagListView.BindEvent( + () => { return m_items.Count; }, + (idx) => { return itemPrefab.gameObject; }, + OnUpdateItem, + (item) => { return item.gameObject.HFGetOrAddSimulateBehaviour(); } + ); } protected override void OnOpen(Action _onOpenComplete) { base.OnOpen(_onOpenComplete); - // 注册事件监听 + // ✅ 注册全局事件监听 + HFFunCatalogue.Event.TCEvent.AddListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); + + // ✅ 初始化数据 + RefreshBagItems(); } protected override void OnClose(Action _onCloseComplete) { - // 取消事件监听 - if (BtnLogin != null) - { - BtnLogin.OnAddClickListener -= OnBtnLoginClick; - } + // ✅ 取消全局事件监听 + HFFunCatalogue.Event.TCEvent.RemoveListener( + enHFCTCEvent.BagItemUpdate, OnBagItemUpdate); + + // ✅ 清理资源 + tips.Clear(); + base.OnClose(_onCloseComplete); } - private void OnBtnLoginClick(GameObject go) - { - // 处理登录逻辑 - } + private void OnBtnClose(GameObject go) => CloseWindow(); + private void OnUpdateItem(SFUI_ScrollRectItem item) { /* 更新列表项 */ } + private void RefreshBagItems() { /* 刷新背包数据 */ } } ``` @@ -58,18 +73,16 @@ public class LoginWindow : AbsHFUIWindow ```csharp // 方式1:直接打开(无回调) -HFFunCatalogue.UIManager.OpenWindow(); +HFFunCatalogue.UIManager.OpenWindow(); // 方式2:打开并获取窗口实例(推荐) -HFFunCatalogue.UIManager.OpenWindow((win, args) => { - // win: 打开的窗口实例 - // args: 传入的额外参数 +HFFunCatalogue.UIManager.OpenWindow((win, args) => { Debug.Log("窗口打开完成"); }); // 方式3:带额外参数打开 object[] extraArgs = new object[] { "param1", 123 }; -HFFunCatalogue.UIManager.OpenWindow((win, args) => { +HFFunCatalogue.UIManager.OpenWindow((win, args) => { // 使用窗口实例 }, extraArgs); ``` @@ -80,12 +93,32 @@ HFFunCatalogue.UIManager.OpenWindow((win, args) => { ### 2.1 生命周期方法 -| 方法 | 调用时机 | 说明 | -|-----|---------|------| -| `OnRunAwake` | 窗口创建时调用一次 | 初始化组件引用 | -| `OnOpen` | 每次打开窗口时调用 | 注册事件、刷新数据 | -| `OnClose` | 每次关闭窗口时调用 | 取消事件、清理资源 | -| `OnDestroy` | 窗口销毁时调用 | 释放资源 | +| 方法 | 调用时机 | 说明 | 推荐操作 | +|-----|---------|------|---------| +| `OnRunAwake` | 窗口创建时调用一次 | 初始化组件引用 | **绑定按钮事件、列表初始化** | +| `OnOpen` | 每次打开窗口时调用 | 注册事件、刷新数据 | **注册全局TC事件、初始化数据** | +| `OnClose` | 每次关闭窗口时调用 | 取消事件、清理资源 | **取消全局TC事件、清理UI状态** | +| `OnDestroy` | 窗口销毁时调用 | 释放资源 | **释放非托管资源** | + +### 2.2 生命周期流程图 + +``` +窗口创建 + ↓ +OnRunAwake() ──→ 初始化组件、绑定按钮事件 + ↓ +窗口打开(可多次) + ↓ +OnOpen() ──→ 注册TC事件、刷新数据 + ↓ +窗口关闭(可多次) + ↓ +OnClose() ──→ 取消TC事件、清理资源 + ↓ +窗口销毁 + ↓ +OnDestroy() ──→ 释放资源 +``` --- @@ -100,7 +133,96 @@ HFFunCatalogue.UIManager.OpenWindow((win, args) => { | `isNeedMask` | 是否需要遮罩 | true | | `maskType` | 遮罩类型 | 半透明 | +### 3.2 层叠配置 + +```csharp +[HFUIWindowLayer(enHFUICanvasLayer.UIWindow, enHFUIWindowLayer.Fixed, isIgnoreCloseByESC = false)] +// ↑ Canvas层级 ↑ 窗口层级 +``` + --- -**版本**: v1.0 +## 四、子组件开发 + +### 4.1 物品槽组件示例 + +```csharp +public class BagItemSlot : AbsHFUIWindow +{ + private SFUI_Button Btn_Item; + private SFUI_Image Img_Icon; + private SFUI_TextMeshProUGUI Txt_Num; + + private Action selectCallback; + private ItemInfo item; + private int m_index; + + protected override void OnRunAwake() + { + base.OnRunAwake(); + Btn_Item = FindUICtrlByName("Btn_Item"); + Img_Icon = FindUICtrlByName("Img_Icon"); + Txt_Num = FindUICtrlByName("Txt_Num"); + + Btn_Item.OnAddClickListener -= OnClick; + Btn_Item.OnAddClickListener += OnClick; + } + + public void SetData(ItemInfo item, ItemInfo nowSelect, int index, Action onSelect) + { + this.item = item; + this.m_index = index; + this.selectCallback = onSelect; + + Img_Icon.HFSetItemIcon(item.ItemConfig.Resource); + Txt_Num.text = item.Info.count.ToString(); + } + + private void OnClick(GameObject go) + { + selectCallback?.Invoke(item, m_index, go.transform.position); + } +} +``` + +### 4.2 提示框组件示例 + +```csharp +public class BagTips : AbsHFUIWindow +{ + private SFUI_Image Img_ItemIcon; + private SFUI_TextMeshProUGUI Txt_ItemName; + private SFUI_TextMeshProUGUI Txt_ItemDesc; + + public ItemInfo Info { get; private set; } + + protected override void OnRunAwake() + { + base.OnRunAwake(); + Img_ItemIcon = FindUICtrlByName("Img_ItemIcon"); + Txt_ItemName = FindUICtrlByName("Txt_ItemName"); + Txt_ItemDesc = FindUICtrlByName("Txt_ItemDesc"); + } + + public void SetData(ItemInfo item) + { + Info = item; + Img_ItemIcon.HFSetItemIcon(item.ItemConfig.Resource); + Txt_ItemName.text = item.ItemConfig.NameCN; + Txt_ItemDesc.text = item.ItemConfig.DesLanguageCN; + } + + public void Clear() + { + Info = null; + Img_ItemIcon.HFClearSprite(); + Txt_ItemName.text = string.Empty; + Txt_ItemDesc.text = string.Empty; + } +} +``` + +--- + +**版本**: v1.1 **适用范围**: GiftGameX项目业务开发人员 \ No newline at end of file diff --git a/03_核心功能/UI组件使用指南.md b/03_核心功能/UI组件使用指南.md index 1382d4a..5c366f1 100644 --- a/03_核心功能/UI组件使用指南.md +++ b/03_核心功能/UI组件使用指南.md @@ -7,37 +7,31 @@ ```csharp SFUI_Button btn = GetComponent(); -// 添加点击事件 +// 添加点击事件(带防重复注册保护) +btn.OnAddClickListener -= OnBtnClick; btn.OnAddClickListener += OnBtnClick; // 添加鼠标进入/退出事件 +btn.OnAddEnterListener -= OnBtnEnter; btn.OnAddEnterListener += OnBtnEnter; + +btn.OnAddExitListener -= OnBtnExit; btn.OnAddExitListener += OnBtnExit; // 设置按钮状态 btn.SetSelfGray(true); // 设置灰色不可用 btn.interactable = false; // 设置不可交互 +btn.SetSelected(true); // 设置选中状态 -void OnBtnClick(GameObject go) -{ - // 处理点击 -} - -void OnBtnEnter(GameObject go) -{ - // 鼠标进入 -} - -void OnBtnExit(GameObject go) -{ - // 鼠标退出 -} +void OnBtnClick(GameObject go) { /* 处理点击 */ } +void OnBtnEnter(GameObject go) { /* 鼠标进入 */ } +void OnBtnExit(GameObject go) { /* 鼠标退出 */ } ``` -### 1.2 SFUI_Text组件 +### 1.2 SFUI_TextMeshProUGUI组件 ```csharp -SFUI_Text txt = GetComponent(); +SFUI_TextMeshProUGUI txt = GetComponent(); // 设置文本 txt.text = "Hello World"; @@ -58,16 +52,20 @@ txt.outlineWidth = 2; ```csharp SFUI_Image img = GetComponent(); -// 加载Sprite -HFFunCatalogue.Sprite.LoadSprite("Icon_Player", sprite => { - img.sprite = sprite; -}); +// 加载Sprite(使用框架封装方法) +img.HFSetItemIcon("Icon_Player"); + +// 加载远程图片 +img.HFSetUrlSprite("https://example.com/icon.png"); // 设置透明度 img.SetAlpha(0.5f); // 设置灰度 img.SetSelfGray(true); + +// 清空Sprite +img.HFClearSprite(); ``` ### 1.4 SFUI_Toggle组件 @@ -76,6 +74,7 @@ img.SetSelfGray(true); SFUI_Toggle toggle = GetComponent(); // 添加状态改变事件 +toggle.onValueChanged.RemoveListener(OnToggleValueChanged); toggle.onValueChanged.AddListener(OnToggleValueChanged); void OnToggleValueChanged(bool isOn) @@ -84,21 +83,38 @@ void OnToggleValueChanged(bool isOn) } ``` -### 1.5 SFUI_ScrollRect组件 +### 1.5 SFUI_ScrollRect组件(列表绑定模式) ```csharp SFUI_ScrollRect scroll = GetComponent(); +List m_items = new List(); -// 设置滚动位置 -scroll.verticalNormalizedPosition = 1f; +// ✅ 列表绑定(推荐方式) +scroll.BindEvent( + () => { return m_items.Count; }, // 数据源数量 + (idx) => { return itemPrefab.gameObject; }, // 实例化模板 + OnUpdateItem, // 更新回调 + (item) => { // 组件获取 + return item.gameObject.HFGetOrAddSimulateBehaviour(); + } +); -// 添加滚动事件 -scroll.onValueChanged.AddListener(OnScrollValueChanged); +// 刷新列表 +scroll.ShowView(); // 显示视图 +scroll.RefreshView(null); // 刷新当前视图 +scroll.ScrollToIndex(0); // 滚动到指定索引 -void OnScrollValueChanged(Vector2 value) +// 添加拖拽事件 +scroll.OnAddBeginDragListener -= OnBeginDrag; +scroll.OnAddBeginDragListener += OnBeginDrag; + +void OnUpdateItem(SFUI_ScrollRectItem item) { - // 处理滚动 + var slot = item.simulateBehaviour as BagItemSlot; + slot.SetData(m_items[item.dataIdx]); } + +void OnBeginDrag(GameObject go) { /* 处理拖拽开始 */ } ``` --- @@ -111,23 +127,18 @@ void OnScrollValueChanged(Vector2 value) | 组件类型 | 示例 | |---------|-----| -| SFUI_Button | BtnLogin | -| SFUI_Text | TxtTitle | -| SFUI_Image | ImgAvatar | +| SFUI_Button | BtnLogin, BtnClose | +| SFUI_TextMeshProUGUI | TxtTitle, TxtNum | +| SFUI_Image | ImgIcon, ImgKuang | | SFUI_Toggle | ToggleSelect | -| SFUI_ScrollRect | ScrollContent | -| SFUI_InputField | InputName | +| SFUI_ScrollRect | BagListView, ScrollContent | +| SFUI_InputField | InputName, InputPassword | +| SFUI_GridLayoutGroup | GridItemContainer | **命名约定**: - 变量名 = 组件名(保持完全一致) - 首字母大写,采用 PascalCase 命名风格 - -**使用示例**: -```csharp -BtnLogin = FindUICtrlByName("BtnLogin"); -TxtTitle = FindUICtrlByName("TxtTitle"); -ImgAvatar = FindUICtrlByName("ImgAvatar"); -``` +- 前缀说明:Btn(按钮)、Txt(文本)、Img(图片)、Toggle(开关)、Scroll(滚动)、Input(输入框)、Grid(网格) ### 2.2 层级结构 @@ -135,10 +146,10 @@ ImgAvatar = FindUICtrlByName("ImgAvatar"); WindowRoot/ ├── bg/ # 背景层 ├── content/ # 内容层 -│ ├── header/ # 头部 -│ ├── body/ # 主体 -│ └── footer/ # 底部 -└── top/ # 顶层(弹窗、提示等) +│ ├── header/ # 头部(标签页、标题) +│ ├── body/ # 主体(列表、内容区) +│ └── footer/ # 底部(操作按钮) +└── top/ # 顶层(弹窗、提示、遮罩) ``` --- @@ -147,14 +158,16 @@ WindowRoot/ | 组件名 | 基类 | 扩展功能 | |-------|------|---------| -| `SFUI_Button` | Button | 灰色状态、多事件委托 | +| `SFUI_Button` | Button | 灰色状态、多事件委托、选中状态 | | `SFUI_Text` | Text | 描边、阴影扩展 | -| `SFUI_Image` | Image | 灰度、透明度快捷设置 | +| `SFUI_TextMeshProUGUI` | TextMeshProUGUI | 继承原生功能 | +| `SFUI_Image` | Image | 灰度、透明度快捷设置、Sprite加载 | | `SFUI_Toggle` | Toggle | 状态改变事件 | -| `SFUI_ScrollRect` | ScrollRect | 滚动优化 | +| `SFUI_ScrollRect` | ScrollRect | **列表绑定模式、滚动优化** | | `SFUI_InputField` | InputField | 输入限制 | | `SFUI_Slider` | Slider | 数值范围限制 | | `SFUI_Dropdown` | Dropdown | 选项管理 | +| `SFUI_GridLayoutGroup` | GridLayoutGroup | 网格布局 | --- @@ -162,13 +175,82 @@ WindowRoot/ ### 4.1 优化建议 -- **对象池复用**:频繁创建的UI元素使用对象池 -- **图集打包**:将小图标打包到SpriteAtlas -- **层级优化**:合理设置Canvas层级 -- **避免过度绘制**:减少重叠UI元素 -- **批量操作**:使用RectTransform批量修改 +| 优化项 | 说明 | 实现方式 | +|-------|------|---------| +| **对象池复用** | 频繁创建的UI元素使用对象池 | 使用 `HFClassEntityManager` | +| **图集打包** | 将小图标打包到SpriteAtlas | 在Unity中配置SpriteAtlas | +| **层级优化** | 合理设置Canvas层级 | 使用 `HFUIWindowLayer` 属性 | +| **避免过度绘制** | 减少重叠UI元素 | 合理设计UI布局 | +| **批量操作** | 使用RectTransform批量修改 | 禁用后批量修改再启用 | +| **列表优化** | 使用SFUI_ScrollRect绑定模式 | 自动复用列表项 | + +### 4.2 列表性能优化示例 + +```csharp +// ✅ 使用SFUI_ScrollRect绑定模式(自动复用) +BagListView.BindEvent( + () => { return m_items.Count; }, + (idx) => { return itemPrefab.gameObject; }, + OnUpdateItem, + (item) => { return item.gameObject.HFGetOrAddSimulateBehaviour(); } +); + +// ✅ 数据变化时只刷新必要项 +private void OnBagItemUpdate(IHFEventArg arg) +{ + // 只在数据实际变化时刷新 + if (HasDataChanged()) + { + BagListView.ShowView(); + } +} +``` --- -**版本**: v1.0 +## 五、子组件开发规范 + +### 5.1 子组件模板 + +```csharp +public class CustomSlot : AbsHFUIWindow +{ + // UI组件 + private SFUI_Button Btn_Item; + private SFUI_Image Img_Icon; + + // 数据和回调 + private Action onSelect; + private ItemInfo item; + + protected override void OnRunAwake() + { + base.OnRunAwake(); + Btn_Item = FindUICtrlByName("Btn_Item"); + Img_Icon = FindUICtrlByName("Img_Icon"); + + // 事件绑定(带防重复保护) + Btn_Item.OnAddClickListener -= OnClick; + Btn_Item.OnAddClickListener += OnClick; + } + + public void SetData(ItemInfo item, Action onSelect) + { + this.item = item; + this.onSelect = onSelect; + + // 更新UI + Img_Icon.HFSetItemIcon(item.ItemConfig.Resource); + } + + private void OnClick(GameObject go) + { + onSelect?.Invoke(item); + } +} +``` + +--- + +**版本**: v1.1 **适用范围**: GiftGameX项目业务开发人员 \ No newline at end of file