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
相关文章:

unity3d:TabView,UGUI多标签页组件,TreeView树状展开菜单
概述 1.最外层DataForm为空壳编辑数据用。可以有多个DataForm,例如福利DataForm,抽奖DataForm 2.Menu层为左边栏层,每个DataForm可以使用不同样式的MenuForm预制体 3.DataForm中使用ReorderList,可排列配置 4.有定位功能…...
go语言map底层及扩容机制原理详解(下)
前言 上文对Go map的底层数据结构有所了解,并对其扩容机制的步骤进行简略的描述。本文将会详细地去解释Go map扩容机制的详细原理。 1. 触发扩容操作 在go语言中,当我们插入一个元素到hmap时,会有以下两种情况: 若元素存在&…...

网络协议二 : 使用Cisco Packet Traceer工具模拟网络环境,集线器,网桥,交换机,路由器,IP,同一网段
1. 安装 Cisco Packet Tracer baidu 网盘地址,感谢大神分享 安装,破解,中文化,都有说明,建议使用7.x的那个版本,感觉比8.x的翻译要完整一点 https://pan.baidu.com/s/18iWBOfhJJRhqgQqdNQcfMQ?pwddcch#…...

Aria2 任意文件写入漏洞
目录 Aria2介绍漏洞描述漏洞复现 Aria2介绍 Aria2是一个在命令行下运行,多协议,多来源下载工具(HTTP / HTTPS,FTP,BitTorrent,Metalink),内建XML-RPC用户界面。Aria提供RPC服务器&a…...

成为git砖家(4): git status 命令简介
1. untracked 和 tracked 状态 Remember that each file in your working directory can be in one of two states: tracked or untracked. Tracked files are files that were in the last snapshot, as well as any newly staged files; they can be unmodified, modified, o…...

2-48 基于matlab的EM算法聚类可视化程序
基于matlab的EM算法聚类可视化程序,通过期望最大化算法(EM)优化类别间距,使得类别间距最大、类内间距最小。输出聚类前后结果及收敛曲线。程序已调通,可直接运行。 2-48 期望最大化算法(EM) 聚类…...
k8s 使用技巧
文章目录 kubectlkubectl 自动补全kubectl 上下文和配置打印当前使用 API 调用过程生成yaml模板强制删除 Pod(即使处于Terminating) kubectl kubectl 自动补全 source < (kubectl completion bash) # setup autocomplete in bash, bash-completion …...

学习笔记-系统框图传递函数公式推导
目录 *待了解 现代控制理论和自动控制理论区别 自动控制系统的组成 信号流图 1、系统框图 1.1、信号线、分支点、相加点 1.2、系统各环节间的连接 1.3、 相加点和分支点的等效移动(比较点、引出点) 2、反馈连接公式推导 2.1、前向通路传递函数…...

C++ - 基于多设计模式下的同步异步⽇志系统
1.项目介绍 项⽬介绍 本项⽬主要实现⼀个⽇志系统, 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.开发环境 • Cent…...

git 相关内容
...

ElasticSearch(es)倒排索引
目录 一、ElasticSearch 二、倒排索引 1. 正向索引 2. 倒排索引 具体细节 1. 文档分析 2. 索引构建 3. 索引存储 4. 词条编码 5. 索引优化 6. 查询处理 示例 总结 3. 正向和倒排 三、总结 倒排索引的基本概念 为什么倒排索引快 一、ElasticSearch Elasticsear…...

【自然语言处理】概论(一):自然语言处理概要
1.1 概论:(一)自然语言处理概要 知识点 自然语言的定义:人类交流使用的,包括口语和书面语的信息交流方式。AI的终极目标:使计算机具备理解(听、读)和生成(说、写&#…...
flask 开始
# 导入flask类 from flask import Flask,request,render_template # 使用flask类来创建一个app对象 # __name__ 代表当前app.py 这个模块 app Flask(__name__) # 创建一个路由和视图函数的映射 url http://127.0.0.1:5000/ app.route("/") def hello_word():return …...

仕考网:公务员可以报考军队文职吗?
公务员可以报考军队文职考试,但是需要满足前提条件。 对于已经与国家、地方的用人单位建立劳动关系的社会人才,在获得当前用人单位的许可后才可以申请报考。 在面试过程中,考生必须出示一份由其用人单位出具的且加盖公章的同意报考证明。一…...
Java整理22
1、动态sql 多条件查询 .xml配置文件中sql语句书写<select id"getEmpByCondition",resultType"Emp">select * from t_emp where <if test"empName ! null and empName! ">empName#{empName}</if><if test"age ! nul…...
leetcode 408周赛 3234. 统计 1 显著的字符串的数量
3234. 统计 1 显著的字符串的数量 题目描述 给你一个二进制字符串 s。 请你统计并返回其中 1 显著 的子字符串的数量。 如果字符串中 1 的数量 大于或等于 0 的数量的 平方,则认为该字符串是一个 1 显著 的字符串 。 思路 一个很显然的思路是,我们…...
容器对比虚拟机有哪些不足?
引言 在当今的云计算和微服务架构中,容器技术已成为不可或缺的一部分。它以其轻量级、高效和快速部署的特性,赢得了广大开发者和运维人员的青睐。然而,正如任何技术都有其两面性,容器技术也不例外。本文将对容器技术在安全性、隔离…...
C# 归并排序
栏目总目录 概念 归并排序是一种分而治之的排序算法。它将一个大数组分成两个小数组,递归地对这两个小数组进行排序,然后将排序好的小数组合并成一个有序的大数组。这个过程一直递归进行,直到数组被拆分成只有一个元素的数组(自然…...

【请求代理】springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能
springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能 一、前言二、解决思路三、基于gateway实现四、基于过滤器Filter实现五、问题总结 **注:本文源码获取或者更多资料,关注公众号:技术闲人**一、前言 在项目开发时会遇到w…...
.NET Core异步编程与多线程解析:提升性能与响应能力的关键技术
在.NET Core中,异步编程和多线程是构建高性能应用程序的核心技能。理解这两个概念不仅可以提升应用程序的响应能力,还能优化资源使用。本文将深入剖析异步编程和多线程的关键知识点,提供代码示例,并附上步骤以帮助理解。 1. 异步…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...