学习笔记
之前项目中有需求要实现构件树,实现文章目录的效果,在空余时间研究出个简陋版的目录树,主要依赖UGUI自带的组件 VerticalLayoutGroup和ContentSizeFitter,其次是通过脚本创建节点
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,然后添加 VerticalLayoutGroup和ContentSizeFitter组件,其中 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、关于预制的设置
加载预制体可以按划分节点(目前我的是直接按目录级别做的预制体,各位大佬可以用自己的方式),首先是总节点:创建一个空物体,将锚点直接设置为锁定一个角(左边上下都可以),然后同样添加 VerticalLayoutGroup和ContentSizeFitter组件,VerticalLayoutGroup组件中的ChildControlsSize的Heigh不用勾选,然后再空物体节点下创建一个Button,用来触发点击事件,如图所示:
所有节点(除开最后的节点)都同样设置,最后的子节点设置直接创建一个button,将锚点锁定左边的一个角,就可以了。最后上个效果图:
码字不易,转载请注明出处,谢谢,希望各位路过的大佬多多指教