先上效果图
本文利用VerticalLayoutGroup组件动态生成目录,目录数据由文件目录转换而成。无需其他设置。只需将TreeMenuCheckBox脚本挂载到某个对象上 然后指定属性面板的值既可。
直接上代码 一共两个脚本 TreeMenuCheckBox负责创建目录 UIMenuClick负责点击事件的监听
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class UIMenuClick : MonoBehaviour ,IPointerUpHandler,IPointerDownHandler,IDragHandler,IBeginDragHandler,IEndDragHandler{
public GameObject Group = null;
public GameObject Identifying = null;
public System.Action<RectTransform> Refresh;
private bool IsDrag = false;
public void OnBeginDrag(PointerEventData eventData)
{
IsDrag = true;
GetParntScrollRect().OnBeginDrag(eventData);
}
public void OnDrag(PointerEventData eventData)
{
GetParntScrollRect().OnDrag(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
IsDrag = false;
GetParntScrollRect().OnEndDrag(eventData);
}
public void OnPointerDown(PointerEventData eventData)
{
}
public void OnPointerUp(PointerEventData eventData)
{
if (IsDrag)
return;
if (Group!=null)
{
Group.SetActive(!Group.activeSelf);
RefreshIdentifying();
Refresh?.Invoke(Group.GetComponent<RectTransform>());
}
}
public static UnityEngine.UI.ScrollRect scrollRect;
public UnityEngine.UI.ScrollRect GetParntScrollRect()
{
if (scrollRect==null)
scrollRect = GameObject.FindObjectOfType<TreeMenuCheckBox>().GetComponent<UnityEngine.UI.ScrollRect>();
return scrollRect;
}
///
/// 刷新标识箭头
///
public void RefreshIdentifying()
{
if (Identifying!=null)
Identifying.transform.localEulerAngles = new Vector3(0, 0, Group ? Group.activeSelf ? -90 : 0 : 0);
}
}
public class TreeMenuCheckBox : MonoBehaviour {
[Header("字体")]
public Font font;
[Header("标识图")]
public Sprite Ide;
public Catalog data = new Catalog();
[Header("窗口根节点")]
public Transform Content;
[Header("默认展开?")]
public bool IsUnfold = false;
void Start () {
if (font==null)
{
Debug.Log("[请设置Font]");
return;
}
#region 通过文件夹获取树状结构数据 也可以通过其他方式给 Catalog 赋值
DirectoryInfo dir = new DirectoryInfo(Application.streamingAssetsPath+"/data");
DirectoryInfo[] dirs = dir.GetDirectories();
for (int i = 0; i < dirs.Length; i++)
{
GetMenus(dirs[i],data.Data);
}
#endregion
Create();
Refresh(Content.GetComponent<RectTransform>());
}
public void GetMenus(DirectoryInfo info, List<Catalog.CatalogData> data)
{
data.Add(new Catalog.CatalogData(info.Name));
DirectoryInfo[] dirs = info.GetDirectories();
for (int i = 0; i < dirs.Length; i++)
{
GetMenus(dirs[i], data[data.Count-1].Data);
}
}
///
/// 创建树形目录
///
public void Create()
{
CreateNode(Content,data.Data);
Debug.Log("创建目录完毕");
}
///
/// 创建节点
///
///
///
public void CreateNode(Transform parnt, List<Catalog.CatalogData> data)
{
for (int i = 0; i < data.Count; i++)
{
#region 创建菜单
GameObject menu = new GameObject(data[i].MenuName);
Image image= menu.AddComponent<Image>();
image.color = Color.gray;
menu.transform.SetParent(parnt);
menu.transform.localScale = Vector3.one;
menu.GetComponent<RectTransform>().sizeDelta = new Vector2(400,50);
#endregion
#region 创建菜单下的文字
GameObject label = new GameObject(data[i].MenuName);
label.transform.SetParent(menu.transform);
label.transform.localScale = Vector3.one;
Text text = label.AddComponent<Text>();
text.font = font;
text.text = data[i].MenuName;
text.fontSize = 26;
text.fontStyle = FontStyle.Normal;
text.alignment = TextAnchor.MiddleCenter;
RectTransform rect = label.GetComponent<RectTransform>();
rect.sizeDelta = new Vector2(400, 50);
#endregion
#region 添加菜单的监听事件
UIMenuClick menuClick = menu.AddComponent<UIMenuClick>();
menuClick.Group = null;
menuClick.Refresh = Refresh;
GameObject identifying = new GameObject("identifying");
Image ima = identifying.AddComponent<Image>();
ima.sprite= Ide;
ima.SetNativeSize();
identifying.transform.SetParent(menu.transform);
identifying.transform.localScale = Vector3.one;
rect = identifying.GetComponent<RectTransform>();
rect.pivot = new Vector2(0.5f,0.5f);
rect.localPosition = new Vector3(-(menu.GetComponent<RectTransform>().sizeDelta.x/2f)-10f- rect.sizeDelta.x/2f, 0);
identifying.SetActive(false);
menuClick.Identifying = identifying;
#endregion
#region 创建当前菜单下的节点
if (data[i].Data.Count>0)
{
GameObject group = new GameObject(data[i].MenuName+ "_Group");
menuClick.Group = group;
group.transform.SetParent(parnt);
group.transform.localScale = Vector3.one;
group.AddComponent<RectTransform>().sizeDelta = new Vector2(400, 0);
VerticalLayoutGroup layoutGroup = group.AddComponent<VerticalLayoutGroup>();
layoutGroup.padding.left = 50;
//group.padding.top = (int)a.GetComponent().sizeDelta.y;
layoutGroup.spacing = 10f;
layoutGroup.childControlHeight = false;
layoutGroup.childControlWidth = false;
group.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
CreateNode(group.transform, data[i].Data);
group.SetActive(IsUnfold);
identifying.SetActive(true);
menuClick.RefreshIdentifying();
LayoutRebuilder.ForceRebuildLayoutImmediate(group.GetComponent<RectTransform>());
}
#endregion
}
}
///
/// 刷新UI布局
///
///
public void Refresh(RectTransform rect)
{
LayoutRebuilder.ForceRebuildLayoutImmediate(rect);
if (rect.parent!=null)
{
if (rect.parent.GetComponent<RectTransform>()!=null)
{
Refresh(rect.parent.GetComponent<RectTransform>());
}
}
}
}
Content 根节点须手动添加VerticalLayoutGroup 组件和ContentSizeFitter组件
本人的ScrollRect宽度为450.
目录数据放在StreamingAssets目录下 大家也可以根据自己的需求做一些改变
补充:少了一点代码
public class Catalog
{
public List<CatalogData> Data = new List<CatalogData>();
public class CatalogData
{
public string MenuName="";
public List<CatalogData> Data = new List<CatalogData>();
public CatalogData(string name)
{
MenuName = name;
}
}
}
Unity3D利用VerticalLayoutGroup组件制作(折叠菜单、树状菜单) 二