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.rhkb.cn/news/386882.html

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

相关文章

stl-set

目录 目录 内部自动有序、不含重复元素 关于能不能自己造一个cmp&#xff0c;还挺复杂。 访问&#xff1a;只能用迭代器且受限 添加元素&#xff1a;没有pushback&#xff0c;用insert 复杂度&#xff1a;ologn ​编辑 查找元素find&#xff08;&#xff09;&#xff1…

C++-入门(下)

一、前言&#xff1a; 目标&#xff1a; 1. C 关键字 2. 命名空间 3. C 输入 & 输出 4. 缺省参数 5. 函数重载 6. 引用 7. 内联函数 8. auto 关键字 (C11) 9. 基于范围的 for 循环 (C11) 10. 指针空值 ---nullptr(C11) 二、目标的实现&#xff1a; 6. 引用&#xff1a;…

后端开发工程师vue2初识的学习

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;JavaWeb关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 什么是Vue&#xff1f; Vue &#xff08;通常指 Vue.js&#xff09;是一个用…

Mybatis批量更新数据库错误

问题&#xff1a;记录一次使用Mybatis批量更新数据库的错误&#xff0c;错误信息&#xff0c;Error updating database. Cause: org.postgresql.util.PSQLException: 错误: 字段 "update_time" 的类型为 timestamp without time zone, 但表达式的类型为 text 建议&am…

【Unity动画】Animation Sequencer:动画制作的革新工具

在Unity游戏开发中&#xff0c;动画是提升玩家体验的关键因素。传统的动画制作方式往往耗时且复杂&#xff0c;但有了Animation Sequencer&#xff0c;这一过程将变得更加直观和高效。本文将介绍Animation Sequencer这一视觉工具&#xff0c;探讨其如何帮助开发者在Unity编辑器…

Ubuntu 20.04.6 安装 Elasticsearch

1.准备 -- 系统更新 sudo apt update sudo apt upgrade -- 安装vim 文本编辑器 sudo apt install vim-- jdk 版本确认 java -versionjdk 安装可以参照&#xff1a;https://blog.csdn.net/CsethCRM/article/details/140768670 2.官方下载Elasticsearch 官方地址&#xff1a;h…

Linux源码阅读笔记14-IO体系结构与访问设备

IO体系结构 与外设通信通常称为输入输出&#xff0c;一般缩写为I/O。在实现外设IO的时候&#xff0c;内核必须处理三个可能出现的问题&#xff1a; 必须根据具体的设备类型和模型&#xff0c;使用各种方法对硬件寻址。内核必须向用户应用程序和系统工具提供访问各种设备的方法…

maven引入了jar包但在class文件里找不到jar包里的类

在工作当中遇到的这个问题&#xff0c;别人引入的jar包&#xff0c;我代码里报错 maven clean 和 maven install 都不管用 检查过了pom文件 检查了maven仓库路径下是否有这个cn.hutool的jar包 都没有找到问题 最终解决办法是手动引入 步骤一&#xff1a;点击左上角file->…

Golang | Leetcode Golang题解之第290题单词规律

题目&#xff1a; 题解&#xff1a; func wordPattern(pattern string, s string) bool {word2ch : map[string]byte{}ch2word : map[byte]string{}words : strings.Split(s, " ")if len(pattern) ! len(words) {return false}for i, word : range words {ch : patt…

基于python的百度迁徙迁入、迁出数据分析(四)

这篇文章是对上篇文章的可获取数据的时间区间的修正&#xff0c;依然通过开发者模式找寻相关数据源&#xff0c;我直接把数据url贴在这里&#xff0c;可以发现里面包含了相对明面上看不到的数据包括&#xff0c;行政区id、春运迁徙数据等&#xff1a;qianxi.cdn.bcebos.com/app…

【C语言报错已解决】“Undefined Reference”

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言&#xff1a; 在开发过程中&#xff0c;我们经常会遇到各种编译错误或运行时错误。其中&#xff0c;“Undefined Referenc…

The Llama 3 Herd of Models 第5部分,结果部分全文

第1-3部分 第4部分 5 Results 我们对Llama 3进行了一系列广泛的评估,调查了:(1)预训练语言模型的性能,(2)后训练语言模型的性能,以及(3)Llama 3的安全特性。我们在下面单独的小节中提出这些评估的结果。 5.1 Pre-trained Language Model 预训练语言模型 在本节中,我们报…

逻辑回归推导

逻辑回归既可以看作是回归算法&#xff0c;也可以看做是分类算法。通常作为分类算法使用&#xff0c;只可以解决二分类问题。 在上述平面中&#xff0c;每个颜色代表一个类别&#xff0c;即有4个类别 将红色的做为一个类别&#xff0c;其他三个类别都统称为其他类别&#xff0…

C#初级——枚举

枚举 枚举是一组命名整型常量。 enum 枚举名字 { 常量1, 常量2, …… 常量n }; 枚举的常量是由 , 分隔的列表。并且&#xff0c;在这个整型常量列表中&#xff0c;通常默认第一位枚举符号的值为0&#xff0c;此后的枚举符号的值都比前一位大1。 在将枚举赋值给 int 类型的…

完成stable将图片转换为二维码

1.创建虚拟环境 conda create -n stable python=3.10.6 2.克隆项目 git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 或者 git clone https://kgithub.com/AUTOMATIC1111/stable-diffusion-webui 3.安装依赖(-i https://pypi.tuna.tsinghua.edu.cn/s…

大数据-54 Kafka 安装配置 环境变量配置 启动服务 Ubuntu配置 ZooKeeper

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

抓包工具——wireshark的使用

​ 什么是wireshark wireshark是一个数据包捕捉程序。和linux下的tcpdump&#xff0c;以及sniffer&#xff0c;Fidder等软件功能类似。按理说&#xff0c;我们的计算机中的网卡设备只会将发给本机的数据包传输到上层进行解析&#xff0c;而其他的数据包会进行丢弃&#xff0c;…

纯原创【车牌识别】基于图像处理的车牌识别——matlab项目实战(含GUI界面)详解

摘要 车牌识别系统乃计算机视觉与模式识别技术于智能交通领域的重要研究课题之一。其作用在于从复杂背景里提取运动中的汽车牌照&#xff0c;进而识别出车牌号码。车牌识别技术在高速公路电子收费、日常停车场管理以及交通违章监控等场景得到广泛运用。它的问世对于维护交通安全…

MongoDB 基础知识

一、为什么学习MongoDB MongoDB解决Mysql 的“三高”问题&#xff1a; 1.对数据库高并发写入需求 2.对海量数据高效率存储访问需求 3.对数据库高扩展和高可用的需求 MongoDB 实际应用&#xff1a; 1.社交场景&#xff0c;比如朋友圈&#xff0c;附近的人的地点的存储 2.…

【JAVA开发笔记】Reids下载、安装、配置-Windows篇(超详细,含Redis可视化管理工具!!!)

目录 1. Redis 简介 2. 下载 Redis 安装包 3. 开启 Redis 服务 4. 配置环境变量 5. Redis 服务注册为系统服务 6. Redis 服务测试和简单使用 7. 下载安装 Redis 管理工具 8. 管理工具连接 Redis 服务器 1. Redis 简介 Redis&#xff08;Remote Dictionary Server&…