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

news2024/11/17 9:45:54

概述

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)
        {
            //实例化创建MenuForm
            StarForce.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 * 位数 + second
        public 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 true
                    TabViewPage 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引用-1
            foreach (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.bundle
            if (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.coloradmin.cn/o/1956053.html

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

相关文章

网址导航系统PHP源码分享

1、采用光年全新v5模板开发后台 2、后台内置8款主题色&#xff0c;分别是简约白、炫光绿、渐变紫、活力橙、少女粉、少女紫、科幻蓝、护眼黑 3、可管理无数引导页主题并且主题内可以进行不同的自定义设置&#xff0c;目前内置16套主题 持续增加中… 4、可单独开发各种插件&a…

【OSCP系列】OSCP靶机-LemonSqueezy(原创)

【OSCP系列】OSCP靶机-LemonSqueezy 原文转载已经过授权 原文链接&#xff1a;Lusen的小窝 - 学无止尽&#xff0c;不进则退 (lusensec.github.io) 一、主机发现 二、端口扫描 1、快速扫描 2、全端口扫描 只有一个80端口 3、版本系统探测 80端口http的apache服务&#xff0…

2年社招冲击字节,一天三面斩获offer

在工作满两年的时间选择了求变&#xff0c;带着运气和实力以社招身份重新看今天的互联网环境&#xff0c;从结果看还是复合预期的。 整个面试的流程还挺快的。周中让招聘专员给投递了简历。问什么时候面试&#xff0c;申请了一个周日&#xff0c;直接安排三面。下周周中就开启…

【神经网络】梯度下降的优化方法【数学公式+代码示例】

文章目录 1、简介2、指数加权平均2.1、公式2.2、代码 3、Momentum⭐3.1、公式演变3.2、代码 4、AdaGrad4.1、计算步骤4.2、代码示例 5、RMSProp5.1、公式5.2、代码5.3、小结 6、Adam6.1、公式和步骤解释⭐6.2、代码⭐6.3、优点 7、何为鞍点8、小结 &#x1f343;作者介绍&#…

Unity3D 转换微信小游戏指引 05 广告内购

Unity3D 转换微信小游戏指引系列&#xff08;第五期 完结&#xff09; 广告 在小程序后台页面找到推广->流量主 开通条件如下&#xff1a; 开通之后&#xff0c;需要接入广告组件。 调用创建广告组件的接口时&#xff0c;需要传入参数 adUnitId&#xff0c;这个是开通流量…

等保测评练习卷20

等级保护初级测评师试题20 姓名&#xff1a; 成绩&#xff1a; 判断题&#xff08;10110分&#xff09; 1. 应根据资产的危害程度对资产进行标识管理&#xff0c;根据资产的价值选择相应的管理措施&#xff08;F&#xff09;不是危…

十七、操作符详解(2)

1.操作符 &#xff08;1&#xff09;按位取反~ C语言中&#xff0c;0的符号位是0。 例&#xff1a;int a 0; ~a -1 按二进制补码取反&#xff0c;符号位也要取反&#xff0c;打印的是原码。 把一个数的二进制位的第n位变成1&#xff1a;a a | (1<<n-1) 应用场合 把…

猫头虎分享:图文创作者发布文章之前需要预览哪些内容?

&#x1f4dd; 猫头虎分享&#xff1a;作为创作者发布文章之前&#xff0c;需要预览哪些内容&#xff1f; 摘要 在发布一篇文章之前&#xff0c;预览是一个至关重要的步骤。确保文章的结构完整性、内容的连贯性以及读者的阅读体验&#xff0c;都是我们需要关注的重点。本文将…

OSPF动态路由协议实验

首先地址划分 一个骨干网段分成三个&#xff0c;r1&#xff0c;r2&#xff0c;r5三个环回网段 &#xff0c;总共要四个网段 192.168.1.0/24 192.168.1.0/26---骨干网段 192.168.1.0/28 192.168.1.16/28 192.168.1.32/28 备用 192.168.1.64/28 192.168.1.64/26---r1环回 192.1…

2024 Java 高分面试宝典 一站式搞定技术面

前言 每年9月和10月&#xff0c;被业界称为“金九银十”&#xff0c;这是人才市场一年中最活跃的时期。此时&#xff0c;企业为了来年的业务扩展&#xff0c;纷纷加大招聘力度&#xff0c;空缺岗位众多&#xff0c;招聘需求集中。同时&#xff0c;初秋的招聘活动也避开酷暑&am…

物理数据库迁移到云上

物理数据库迁移到云上 物理数据库迁移到云上通常需要以下步骤: 评估和规划 评估物理数据库的需求和约束条件&#xff0c;确定迁移的目标。考虑数据库大小、性能要求、数据复杂性等因素&#xff0c;选择合适的云服务提供商和服务模式。 设置云环境 在云平台上创建适当的虚拟…

特朗普比特币演讲:“梭哈”比特币,“抵制”数字美元!懂王新晋喊单王:比特币冲上月球,永远不要出售比特币!

2024年7月28日&#xff0c;在比特币大会(Bitcoin 2024)上&#xff0c;前总统特朗普发表了一场激情澎湃的演讲&#xff0c;放出了一系列令人振奋的政策诺言&#xff0c;引发了全场加密货币支持者的热烈掌声。特朗普表示&#xff0c;如果再次当选总统&#xff0c;他将采取一系列强…

前端框架 element-plus 发布 2.7.8

更新日志 功能 组件 [级联选择器 (cascader)] 添加持久化属性以提升性能 (#17526 by 0song)[日期选择器 (date-picker)] 类型添加月份参数 (#17342 by Panzer-Jack)[级联选择器 (cascader)] 添加标签效果属性 (#17443 by ntnyq)[加载 (loading)] 补充加载属性 (#17174 by zhixi…

Python数值计算(3)

这次说一说构造均分向量的操作。 1. 造轮子 在数值计算中 &#xff0c;我们通常要在区间[a,b]之间产生指定步长或者给定采样点的一组等差数列&#xff08;或者说是一个向量&#xff09;&#xff0c;例如MATLAB中的linspace函数&#xff0c;linspace(a,b,n)在区间[a,b]之间产生…

【神经网络】正则化缓解过拟合-Dropout

文章目录 1、简介2、Dropout 层的原理和使用3、小结 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初步涉猎人工智能和前端开发。 &#…

C#中的wpf基础

在WPF中&#xff0c;Grid 是一种非常强大的布局控件&#xff0c;用于创建网格布局。它允许你将界面划分为行和列&#xff0c;并将控件放置在这些行和列中。 以下是一些关键点和示例&#xff0c;帮助你理解 WPF 中的 Grid&#xff1a; 基本属性 RowDefinitions&#xff1a;定义…

AI 抠图工具 | 鲜艺AI抠图 v2.1 绿色版

&#x1f389;&#x1f389; 各位小伙伴们&#xff0c;你们是不是还在为抠图烦恼&#xff1f;别担心&#xff0c;鲜艺AI抠图神器来拯救你啦&#xff01;基于RMBG-1.4黑科技开发&#xff0c;免费使用&#xff0c;本地操作&#xff0c;告别繁琐登录和联网困扰&#xff01;&#x…

【数据结构进阶】AVL树

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; C || 数据结构 目录 &#x1f308;前言&#x1f525;AVL树的概念&#x1f525;AVL树的自实现AVL树结点的定义AVL树需实现的函数接口AVL树的插入AVL树的旋转右单旋左单旋左右双旋右左双旋…

【Docker虚拟机】在极空间上快速部署智能家居自动化平台『Home Assistant 』

【Docker&虚拟机】在极空间上快速部署智能家居自动化平台『Home Assistant 』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 前段时间被粉丝问到怎么在极空间上部署Home Assistant&#xff0c;之前只是使用命令在威联通上部署过&#xff0c;所以最近正好有时间&#xff0c;在…

Ethernet

目录 1. Physical Layer(PHY)2. MAC2.1. MAC帧格式2.2. MAC地址与IP地址3. RGMII接口FPGA实现以太网(一)——以太网简介 以太网(Ethernet)是指遵守 IEEE 802.3 标准组成的局域网通信标准, IEEE 802.3 标准规定的主要是OSI参考模型中的物理层(PHY)和数据链路层中的介质访问控…