Unity UGUI目录树实现

学习笔记

之前项目中有需求要实现构件树,实现文章目录的效果,在空余时间研究出个简陋版的目录树,主要依赖UGUI自带的组件 VerticalLayoutGroupContentSizeFitter,其次是通过脚本创建节点
1、首先是Hierarchy面板中创建Canvas画布,然后创建一个Image,改名为ScrollRect,然后添加Mask遮罩组件,然后添加ScrollRect组件,如图所式:在这里插入图片描述2、创建完后开始创建子节点,创建一个空物体,可以命名为Tree,这个是用来挂载脚本(当然挂其它地方也一样),然后将Tree拖拽到ScrollRect中的ScrollRect组件的Viewport位置赋值,然后在同节点下创建一个Scrollbar,然后同样拖拽到ScrollRect中的ScrollRect组件的VerticalScrollbar位置赋值,创建完后如图所示:在这里插入图片描述
3、然后选中Tree,在其节点下创建一个空物体,将其命名为Content,然后修改RectTransform中的属性Pivot将Y值从0.5改为1,然后添加 VerticalLayoutGroupContentSizeFitter组件,其中 VerticalLayoutGroup组件中的ChildControlsSize的Heigh勾选,ContentSizeFitter则是将VerticalFit模式选择为PreferredSize,如图所示:
在这里插入图片描述
4、代码实现,通过三个脚本控制,ContentSizemanger,ConentBaseTree,ComponentTree(命名比较乱,不要在意):
ContentSizemanger.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 节点刷新
/// </summary>
public class ContentSizemanger : MonoBehaviour
{
    private static readonly object _syslock = new object();//多线程下确保只有单个实例
    private static ContentSizemanger instaces;
    public static ContentSizemanger Instaces{ get { lock (_syslock) return instaces; } set { instaces = value; } }

    private RectTransform myContentSizeFitter;

    void Awake()
    {
        Instaces = this;
        myContentSizeFitter = GetComponent<RectTransform>();
    }

    public void ContentRefresh()
    {
        LayoutRebuilder.ForceRebuildLayoutImmediate(myContentSizeFitter);
    }
}

ConentBaseTree.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 布局刷新
/// </summary>
public class ConentBaseTree : MonoBehaviour
{
    bool isDown=true;
    private ContentSizeFitter[] myContent=new ContentSizeFitter[2];
    private RectTransform[] myrRect=new RectTransform[2];

    private void Start()
    {
        myrRect[0] = transform.parent.GetComponent<RectTransform>();
        myrRect[1] = myrRect[0].transform.parent.GetComponent<RectTransform>();
    }

    public void PanerTreeLayout(Transform item)
    {
        Button tree_btn = item.GetComponent<Button>();
        Transform tree_child = (tree_btn.transform.childCount != 0) ? tree_btn.transform.GetChild(0) : tree_btn.transform;
        string str_Name = tree_child.GetComponent<Text>().text;
        tree_btn.onClick.AddListener(()=> { Tree_Active(); });
    }

    private void Tree_Active()//节点显示或隐藏
    {
        ContentSizemanger.Instaces.ContentRefresh();
        if (transform.childCount >= 2)
        {
            for (int i = 1; i < transform.childCount; i++)
            {
                transform.GetChild(i).gameObject.SetActive((isDown) ? true : false);
            }

            isDown = (isDown) ? false : true;
        }
       
        LayoutRebuilder.ForceRebuildLayoutImmediate(myrRect[0]);//动态刷新RectTransform
        LayoutRebuilder.ForceRebuildLayoutImmediate(myrRect[1]);//动态刷新RectTransform
    }
}

ComponentTree.cs这个则是控制节点生成

using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using LitJson;
using System;
/// <summary>
/// 生成
/// </summary>
public class ComponentTree : MonoBehaviour
{
    public Transform firstLevel_panert;
    public Transform panera_Root;//总节点
    public Transform child_Button;//最末级构件按钮
    private Text title_Name;//构件树标题

    private ConentBaseTree conentBaseTree;//UI刷新
    public Transform secondary_Panert;//第二级目录
    private Text treesecond_Name;//第二级节点名称
    public Transform thirdlevel_Panert;//第三级目录
    private Text treethird_Name;//第三级节点名称

    void Start()
    {
        GenerateTree();
    }

    private void GenerateTree()
    {
        Transform paneraTree = SetTransform(panera_Root, title_Name, "总目录");//生成总节点
        paneraTree.parent = firstLevel_panert.transform;//设置父节点
        SetRect(paneraTree);
        conentBaseTree = paneraTree.GetComponent<ConentBaseTree>();
        conentBaseTree.PanerTreeLayout(paneraTree.GetChild(0));//赋值父节点方法

        for (int i = 0; i < 2; i++)//创建二级目录
        {
            Transform tree_second = SetTransform(secondary_Panert, treesecond_Name, "第" + i.ToString() + "节目录");
            tree_second.parent = paneraTree.transform;
            SetRect(tree_second);
            tree_second.gameObject.SetActive(false);

            Transform second_root = tree_second.GetChild(0);
            conentBaseTree = tree_second.GetComponent<ConentBaseTree>();
            conentBaseTree.PanerTreeLayout(second_root);//赋值父节点方法

            for (int t = 0; t < 5; t++)//创建三级目录
            {
                Transform tree_third = SetTransform(thirdlevel_Panert, treethird_Name, t.ToString());
                tree_third.parent = tree_second.transform;
                SetRect(tree_third);
                tree_third.gameObject.SetActive(false);
            }
        }

        RectTransform rectTransform = firstLevel_panert.GetComponent<RectTransform>();
        LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
    }

    private Transform SetTransform(Transform level_panert, Text tree_name, string name)
    {
        Transform tree = GameObject.Instantiate(level_panert);
        Transform item = tree.transform.GetChild(0);
        tree_name = (item.childCount != 0) ? item.GetChild(0).GetComponent<Text>() : item.GetComponent<Text>();
        tree_name.text = name;
        return tree;
    }
    private RectTransform SetRect(Transform tree_three)
    {
        RectTransform childrect = tree_three.GetComponent<RectTransform>();
        childrect.anchoredPosition3D = new Vector3(childrect.anchoredPosition3D.x, childrect.anchoredPosition3D.y, 0);
        childrect.localScale = new Vector3(1, 1, 1);
        return childrect;
    }
}

其中ComponentTree随意挂载,ContentSizemanger必须挂载在Content节点上,ConentBaseTree则挂载在加载的预制体上
5、关于预制的设置
加载预制体可以按划分节点(目前我的是直接按目录级别做的预制体,各位大佬可以用自己的方式),首先是总节点:创建一个空物体,将锚点直接设置为锁定一个角(左边上下都可以),然后同样添加 VerticalLayoutGroupContentSizeFitter组件,VerticalLayoutGroup组件中的ChildControlsSize的Heigh不用勾选,然后再空物体节点下创建一个Button,用来触发点击事件,如图所示:在这里插入图片描述
所有节点(除开最后的节点)都同样设置,最后的子节点设置直接创建一个button,将锚点锁定左边的一个角,就可以了。最后上个效果图:
在这里插入图片描述
码字不易,转载请注明出处,谢谢,希望各位路过的大佬多多指教

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值