unity3d:TabView,UGUI多标签页组件,TreeView树状展开菜单

概述

1.最外层DataForm为空壳编辑数据用。可以有多个DataForm,例如福利DataForm,抽奖DataForm
2.Menu层为左边栏层,每个DataForm可以使用不同样式的MenuForm预制体
3.DataForm中使用ReorderList,可排列配置
4.有定位功能,跳转到对应页签
5.DataForm具有树状图管理,1级,2级菜单
6.PageForm为每个页签的具体生命周期脚本,由DataForm控制。需要显示时如果没加载过,从资源加载。

TabViewDataForm

树状图数据

每个主UI使用共用的脚本,用于编辑左边页签数据结构。主UI,即分配ID,可以通过UI管理器加载出来。例如福利主UI,其中包含多个子页签
树状图菜单分为3种,1级无展开,1级带展开,2级
树状图数据

  public List<TabView> m_listItem = new List<TabView>(4);/// <summary>/// 面板上编辑TreeView数据,包含1级,2级/// </summary>[System.Serializable]public class TabView{public TabViewItem m_item;//1级public List<TabViewItem> m_listSubItem = null; //2级列表}//每个菜单的data[System.Serializable]public class TabViewItem{public string m_name; //无用字段public string m_assetPath; //资源路径,从Assets/开始public string m_dicKey; //字典key,用于多语言public string m_chName; //中文注释名,程序不用,策划可以面板上看排列}

使用ReorderableList自定义面板编辑
在这里插入图片描述

生成TabView枚举

从外部需要跳转到主UI树状图的某个菜单,给1级,2级菜单分配唯一id
规则为枚举名为预制体名+TabViewForm
枚举里每项为这个页签的assetPath路径中的预制体名。例如assetPath 为 Assets/TestTabView0_0.prefab,那么名字为TestTabView0_0
值每个页签first * 1000+ second。
每次如果UI预制体修改了树状图顺序,重新生成一遍TabView枚举

    public enum TestTabViewMainFormTabViewEnum {TestTabView0_0 = 0,TestTabView0_1 = 1,TestTabView1_0 = 1000,TestTabView1_1 = 1001,TestTabView2 = 2000,}

加载TabViewMenuForm

主UI(即绑定TabViewDataForm)的空格UI实例化,OnInit加载TabViewMenuForm预制体

        protected override void OnInit(object userData){//实例化创建MenuFormStarForce.GameEntry.Resource.LoadAsset(m_menuFormAssetPath, m_LoadResourceCallbacks);}加载完毕,设置跳转参数到MenuForm中private void LoadAssetSuccessCallback(string AssetName, object asset, float duration, object userData){if (AssetName == m_menuFormAssetPath){m_menu = inObj.GetComponent<TabViewMenuForm>();m_menu.SetInfo(this, m_openFistIdx, m_openSecondIdx);m_assetMenu = obj;//保存Menu预制体加载在内存中的asset,用于UI销毁时引用-1}

打开第几菜单并传递参数

主UI打开时传入参数

    //打开TabView,可定位第几个public class TabViewOpenData{public int m_idx; //打开第几个 first * 位数 + secondpublic object m_param; //额外参数,传递给page}
TabViewOpenData openData = new TabViewOpenData();
openData.m_idx = (int)TestTabViewMainFormTabViewEnum.TestTabView1_1;
openData.m_param = "1234";
StarForce.GameEntry.UI.OpenUIFormById((int)UIFormId.TestTabViewMainForm, openData);

在主UI打开时
如果MenuForm加载过,调入传入参数idx,确定打开第几个
如果MenuForm未加载过,保存参数,等加载完成回调后打开第几个

        protected override void OnOpen(object userData){base.OnOpen(userData);DataInit();if (userData != null){UIFormParams openUserData = userData as UIFormParams;if (openUserData.UserData != null){m_openData = openUserData.UserData as TabViewOpenData;GetFirstSecondByIdx(m_openData.m_idx, out m_openFistIdx, out m_openSecondIdx);if (m_menu != null){//已经加载成功过menu,直接用。否则等加载结束调用m_menu.Select(m_openFistIdx, m_openSecondIdx);}}else{//没有传入参数,默认打开第0个m_openFistIdx = 0;m_openSecondIdx = 0;if (m_menu != null){//已经加载成功过menu,直接用。否则等加载结束调用m_menu.Select(m_openFistIdx, m_openSecondIdx);}}}

切换Page

树状菜单点击时或者传入参数打开时
如果跟上次点击idx不一致,把上Page置为false,判断idx的page是否存在,存在即置为true,不存在即等待加载

        public void OnClickMenu(int first,int second){PublicFunc.Log($"点击菜单{first}-{second}");int key = GetIdxByFirstSecond(first, second);if (key != m_lastClickIdx){SetPageActive(m_lastClickIdx, false);if (SetPageActive(key, true) == false){//操作当前页打开失败,说明未加载Page,执行加载逻辑TabViewItem tabViewItem = GetDataByFirstSecond(first, second);m_nameSetActiveAfterLoad = tabViewItem.m_assetPath; //这个每次点都只会让最新的SetActive trueTabViewPage tabViewPage = new TabViewPage();tabViewPage.m_firstIdx = first;tabViewPage.m_secondIdx = second;tabViewPage.m_data = tabViewItem;StarForce.GameEntry.Resource.LoadAsset(tabViewItem.m_assetPath, m_LoadResourceCallbacks, tabViewPage);}m_lastClickIdx = key;}//只要点击过menu一次,不管是主动点,还是传递参数点。open参数都需要无效m_openData = null;}

销毁时所有asset引用-1

这里不是直接把ab加载出来的asset卸载,而是把asset的引用-1,然后为0的asset会放入待回收池,等待回收池容量满时,卸载未使用的ab(即ab所有加载出来的asset引用= 0)

        protected override void OnDestroy(){base.OnDestroy();//已经加载过的page的asset 进行asset引用-1foreach (var item in m_dicPage){if (item.Value.m_asset != null){StarForce.GameEntry.Resource.UnloadAsset(item.Value.m_asset);}}//Menu.asset 引用 - 1,如果别的UI还复用此Menu,如果引用不为0,不会卸载Menu.bundleif (m_assetMenu != null){StarForce.GameEntry.Resource.UnloadAsset(m_assetMenu);}m_LoadResourceCallbacks = null;}

TabViewMenuForm

所有界面可复用几个MenuForm,Menu具有不同的样式,即UI图片不同。注意不可使用TabViewMenuForm的OnEnable,OnDisable,因为生命周期是跟随主UI的,因为主UI的OnOpen,OnClose中去调用MenuForm

创建Menu

Menu分为3种,1级无展开,1级带展开,2级
滚动层使用自动布局
在这里插入图片描述

FirstMenu使用布局高度
在这里插入图片描述

SecondMenu使用布局高度
在这里插入图片描述

加载时按照索引加载

        void CreateAllMenu(){for (int i = 0; i < m_dataForm.m_listItem.Count; i++){GameObject first = GameObject.Instantiate(m_objFirstMenu, m_transContent);TabViewMenuItem item = first.GetComponent<TabViewMenuItem>();item.m_firstIdx = i;item.m_secondIdx = 0;first.SetActive(true);TabViewItem firstData = m_dataForm.m_listItem[i].m_item;item.SetData(firstData);item.m_isExpend = false;item.m_isSelect = false;m_dicUIFirstMenu.Add(item.m_firstIdx, item);int childCount = 0;if (m_dataForm.m_listItem[i].m_listSubItem != null){List<TabViewMenuItem> listSub = new List<TabViewMenuItem>(4);m_dicUISecondMenu.Add(item.m_firstIdx, listSub);childCount = m_dataForm.m_listItem[i].m_listSubItem.Count;for (int secondIdx = 0; secondIdx < m_dataForm.m_listItem[i].m_listSubItem.Count; secondIdx++){GameObject second = GameObject.Instantiate(m_objSecondMenu, m_transContent);

并把1级(有展开与无展开),2级记录到字典中

        public Dictionary<int, TabViewMenuItem> m_dicUIFirstMenu = new Dictionary<int, TabViewMenuItem>(8); //1级字典public Dictionary<int, List<TabViewMenuItem>> m_dicUISecondMenu = new Dictionary<int, List<TabViewMenuItem>>(8); //2级

在这里插入图片描述

跳转定位

        public void Select(int firstIdx = 0, int secondIdx = 0){//全部1级收缩,ui恢复为默认状态InitFirstUI();//新的first 打开TabViewMenuItem newFirst;if (m_dicUIFirstMenu.TryGetValue(firstIdx, out newFirst)){ExpendSecond(firstIdx, true);newFirst.SetExpend(true);newFirst.SetSelect(true);}//如果存在2级,选中SelectSecond(firstIdx, secondIdx, true);m_dataForm.OnClickMenu(firstIdx, secondIdx);JumpTo(firstIdx);m_lastFirstMenu = firstIdx;m_lastSecondMenu = secondIdx;}

树状图滚动层作为相应处理,把fist置为可视区第一行
1.如果contentHeight <= 可视区height,不处理
2.如果需要定位的menu在最后面,只需要contentHeight - 可视区height 即为content需要上拉的高度

        //跳转到第几项public void JumpTo(int firstIdx){float cotentHeight = m_transContent.GetComponent<RectTransform>().rect.height;float diffY = m_firstMenuHeight * firstIdx;Vector3 oldPos = Vector3.zero;if (cotentHeight <= m_viewHeight){diffY = 0;}else if (m_viewHeight - (cotentHeight - diffY) > 0){//最后一行超过了diffY = cotentHeight - m_viewHeight;}oldPos.y += diffY;m_transContent.localPosition = oldPos;

点击处理

1.点击第一级无展开,即向主UI传递,加载/显示Page
2.点击第一级展开,作为展开,收缩处理。如果展开,判断每次主UI打开,有没选择过该First下的某个,没有选中Second = 0的,向主UI传递。如果选择过,选择上次选中的second向主UI传递
3.点击2级,向主UI传递

TabViewPageForm

所有的page,继承于此。作用
1.用于控制生命周期,跟Menu一样,不能使用OnEnable,OnDisable,会导致数据有问题,即主UI被覆盖,然后关闭别的UI,恢复主UI,不该初始化时初始化了数据
2.传递到主UI的参数最终传递到Page

    public class TabViewPageForm : MonoBehaviour{public virtual void OnInit(object userData = null){}public virtual void OnOpen(object userData = null){}

继承PageForm的类示例

    public class TestTabViewPageForm : TabViewPageForm{public UnityEngine.UI.Text m_text;public override void OnInit(object userData = null){}public override void OnOpen(object userData = null){if (userData != null){string param = userData as string;m_text.text = param;PublicFunc.Log($"传入参数{param}");}}

在TabViewDataForm中如果打开主UI第一次跳转page,在加载/显示时会传递参数

if (m_openData != null)
{if (m_openData.m_idx == idx){page.OnInit(m_openData.m_param);page.OnOpen(m_openData.m_param);m_openData = null;}else{page.OnInit();page.OnOpen();}
}

流程图

在这里插入图片描述

效果演示

跳转1_1,并传入参数1234

TabViewOpenData openData = new TabViewOpenData();
openData.m_idx = (int)TestTabViewMainFormTabViewEnum.TestTabView1_1;
openData.m_param = "1234";
StarForce.GameEntry.UI.OpenUIFormById((int)UIFormId.TestTabViewMainForm, openData);

在这里插入图片描述

点击不同页签,切换PageView
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/51482.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Clickhouse 生产集群部署(Centos 环境)

文章目录 机器环境配置安装 JDK 8安装 zookeeperClickhouse 集群安装rpm 包离线安装修改全局配置zookeeper配置Shard和Replica设置image.png添加macros配置启动 clickhouse启动 10.82.46.135 clickhouse server启动 10.82.46.163 clickhouse server启动 10.82.46.218 clickhous…

《InheriBT行为树》For Unity

InheriBT: Unity Editor中的行为树编辑框架 行为树&#xff08;Behavior Tree&#xff09;是一种广泛应用于人工智能&#xff08;AI&#xff09;领域的决策模型&#xff0c;特别是在游戏开发中。行为树通过分层结构和节点的组合&#xff0c;实现了复杂行为的简洁表达。然而&am…

CPU350% JVM GC频繁并GC不掉EXCEL导出

背景&#xff1a; 有个Excel导出的需求&#xff0c;测试的时候&#xff0c;只要连续导出大量的数据就会导致FAT机器反请求反应迟钝&#xff0c;甚至卡死&#xff0c;无法恢复。 排查&#xff1a; 1 跳板机跳到机器上&#xff0c;查看 项目 ipd 执行ps -ef | grep 项目名称.j…

虚拟机Ubuntu20.04 利用串口调试机械臂

虚拟机Ubuntu20.04 利用串口调试机械臂 串口库问题 由于机械臂使用的是串口进行驱动控制&#xff0c;在python中相关的串口库为serial和pyserial两个&#xff0c;这里我曾踩过雷同时安装了serial与pyserial两个库&#xff0c;导致报错如下所示&#xff1a; AttributeError: m…

数据结构:(1)线性表

一、基本概念 概念&#xff1a;零个或多个数据元素的有限序列 元素之间是有顺序了。如果存在多个元素&#xff0c;第一个元素无前驱&#xff0c;最后一个没有后继&#xff0c;其他的元素只有一个前驱和一个后继。 当线性表元素的个数n&#xff08;n>0&am…

使用Spring Boot与Spire.Doc实现Word文档的多样化操作

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 使用Spring Boot与Spire.Doc实现Word文档的多样化操作具有以下优势&#xff1a; 强大的功能组合&#xff1a;Spring Boot提供了快速构建独立和生产级的Spring应用程序的能力&#xff0c;而Spire.Doc则…

OSError: You are trying to access a gated repo.解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

『 Linux 』用户态与内核态的转换机制及信号检测时机

文章目录 用户态与内核态进程地址空间操作系统的本质 信号的处理时机 用户态与内核态 进程在执行代码的过程中代码必定涉及用户代码,库函数代码及操作系统内核代码; 以简单的printf()函数为例,该函数必定为先执行用户的代码即知道需要调用printf()函数,再执行库(如libc)中的代码…

Java线程同步与通信:wait(), notify(), notifyAll(), sleep()

Java线程同步与通信&#xff1a;wait&#xff08;&#xff09;, notify&#xff08;&#xff09;, notifyAll&#xff08;&#xff09;, sleep&#xff08;&#xff09; 1. wait()2. notify()3. notifyAll()4. sleep()4、总结 &#x1f496;The Begin&#x1f496;点点关注&…

一文带你读懂TCP

文章目录 1 TCP协议1.1 TCP 基础1.1.1 TCP 特性1.2.2 TCP连接数 1.2 TCP 头1.2.1 TCP 头格式1.2.2 MTU&#xff0c;MSS&#xff0c;分片传输 1.3 TCP 连接三路握手1.4 TCP 断开四次挥手1.5 SYN攻击和防范1.6 重传机制1.6.1 超时重传1.6.2 快速重传1.6.3 SACK 1.7 滑动窗口1.8 流…

Linux基础复习(二)

前言 本文介绍了一下Linux命令行基本操作及网络配置 一、 命令行提示含义 [当前用户主机名 工作目录]$ 若当前用户是root&#xff0c;则最后一个字符为# 否则&#xff0c;最后一个字符为$ 二、常用Linux命令及其解释 修改主机名 一般在创建一台主机后会使用hostname相关命…

在生信分析中大家需要特别注意的事情​

在生信分析中大家需要特别注意的事情 标准的软件使用和数据分析流程 1. 先看我的b站教学视频 2. 先从我的百度网盘把演示数据集下载下来&#xff0c;先把要运行的模块的演示数据集先运行一遍 3. 前两步都做完了&#xff0c;演示数据集也运行成功了&#xff0c;并且知道了软件…

ajax请求成功但不执行success-function回调函数

目录 一、问题分析 二、处理问题 一、问题分析 在测试员工管理系统的登录和注册代码时&#xff0c;登录一切正常&#xff0c;就是注册成功后没有跳转页面&#xff0c;后面发现是success-function回调函数没有正常执行。原因主要是前端和后端交流的数据格式不一致&#xff0c;…

【C++BFS算法 二分查找】2812. 找出最安全路径

本文涉及知识点 CBFS算法 C二分查找 LeetCode2812. 找出最安全路径 给你一个下标从 0 开始、大小为 n x n 的二维矩阵 grid &#xff0c;其中 (r, c) 表示&#xff1a; 如果 grid[r][c] 1 &#xff0c;则表示一个存在小偷的单元格 如果 grid[r][c] 0 &#xff0c;则表示一…

Windows配置AirSim过程(学习过程)

一、概述 因为需要在虚幻引擎当中使用AirSim&#xff0c;在Windows系统上进行操作&#xff0c;根据官方网站的操作过程&#xff0c;进行了配置&#xff0c;这里作为自己配置过程的记录。 二、具体过程 &#xff08;一&#xff09;系统版本 操作系统是Windows11&#xff0c;Ai…

昇思MindSpore 应用学习-RNN实现情感分类-CSDN

RNN实现情感分类 AI代码解析 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入…

HALCON如何创建本地函数

HALCON中有本地函数(.hdev)、外部函数(HDevelop函数文件.hdvp)和库函数(.hdpl) 本地函数(.hdev)&#xff1a;创建后仅在当前程序文件中使用&#xff1b; 外部函数(HDevelop函数文件.hdvp)&#xff1a;创建后可以在其他程序文件中复用&#xff0c;默认保存在…/ procedures/下…

vue实现滚动条下滑时隐藏导航栏,上滑时显示导航栏

效果展示 思路 监听滚动事件&#xff0c;记录上次的滚动距离&#xff0c;与最新滚动距离做对比&#xff0c;如果为正&#xff0c;说明滚动距离距顶值scrollTop变大&#xff0c;用户正在向下滚动页面&#xff0c;此时隐藏&#xff0c;反之则反&#xff0c;隐藏就是top值给他负导…

【C++语言】C++11新特性(1)

一、统一的列表初始化 1.1 {} 初始化 在C98中&#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如&#xff1a; struct Point {int _x;int _y; };int main() {int array1[] { 1, 2, 3, 4, 5 };int array2[5] { 0 };Point p { 1, 2 };ret…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 项目排期安排(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…