前言
如果有更好的写法或是代码有什么错误等等,还请大佬教教我。
最终效果
大概就这么个效果,使用的Scroll View做的,我使用的是中文版,看不懂英文,没得法
准备
预制件(各预制件结构在文中会有,顺着看就好):
脚本:
一、创建Scroll View对象
二、修改Scroll View对象中的组件属性,并挂载MenuData脚本
将Scroll View改名为MenuView
MenuView对象结构:
创建Menus.cs脚本文件,脚本内容为:
using System.Collections.Generic;
namespace Assets.Scripts.Models
{
public class Menus
{
public int Id { set; get; }
public string MenuName { set; get; }
public List<MenuChilds> MenuChilds { set; get; }
}
public class MenuChilds
{
public int Id { set; get; }
public string MenuChildName { set; get; }
}
}
创建MenuData.cs脚本文件并挂载,脚本内容(此脚本相当于菜单的数据源):
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MenuData : MonoBehaviour
{
public List<Menus> menus = new List<Menus>();
public ContentData contentData;
void Start()
{
#region 菜单列表数据,后续替换为从数据库查询
for (int i = 0; i < 8; i++)
{
List<MenuChilds> MenuChilds = new List<MenuChilds>();
for (int j = 0; j < 7; j++)
{
MenuChilds.Add(new MenuChilds()
{
Id = j + 1,
MenuChildName = $"第{j + 1}个"
});
}
Menus Menu = new Menus()
{
Id = i + 1,
MenuName = $"第{i + 1}个菜单",
MenuChilds = MenuChilds
};
menus.Add(Menu);
}
#endregion
//将菜单列表数据传给Content对象
contentData.SetMenu(menus);
}
}
这个感觉有点多余,但还是把这个脚本放在了最外层,或许什么时候就用到了也说不定
三、修改MenuView的子对象Content对象的组件属性
Content Size Fitter和Vertical Layout Group这两个组件需要点击添加组件自己添加
创建并挂载ContentData.cs脚本文件,脚本内容:
(此脚本用于创建一级菜单,并提供菜单无参委托事件,方便在点击之前和点击之后做一些操作)
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ContentData : MonoBehaviour
{
private Transform oldTransform; //上一个点击的按钮
public delegate void BtnClickDelegate();
/// <summary>
/// 一级菜单列表
/// </summary>
public List<Menus> Menus;
/// <summary>
/// 一级菜单预制件
/// </summary>
public GameObject menuPrefab;
/// <summary>
/// 添加一级菜单
/// </summary>
/// <param name="menus"></param>
public void SetMenu(List<Menus> menus)
{
Menus = menus;
for (int i = 0; i < Menus.Count; i++)
{
GameObject towMenuBtnObj = Instantiate(menuPrefab, transform);
towMenuBtnObj.GetComponent<OneMenuBtn>().SetMenuData(Menus[i]);
}
}
#region 一级菜单点击事件(委托)
public void BtnClick(BtnClickDelegate btnClickDelegate, Transform newTransform)
{
bool equals = false;
if (oldTransform != null)
{
equals = oldTransform.Equals(newTransform) || newTransform == null;
if (!equals)
{
//关闭上次点击的菜单
oldTransform.GetComponent<OneMenuBtn>().OnBtnClick();
}
}
oldTransform = newTransform;
//执行委托方法,由子对象的OneMenuBtn脚本调用
if (newTransform != null)
{
btnClickDelegate?.Invoke();
if (equals)
{
RemoveOldTransform();
}
}
}
#endregion
#region 关闭菜单
/// <summary>
/// 关闭菜单
/// </summary>
public void RemoveOldTransform()
{
BtnClick(null, null);
}
#endregion
}
四、创建一级菜单预制件,并修改对象组件的属性
一级菜单预制件结构
一级菜单预制件属性
创建并挂载OneMenuBtn.cs脚本文件,脚本内容:
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class OneMenuBtn : MonoBehaviour
{
private ChildContentData childContentData; //ChildContent对象的ChildContentData脚本组件
private Image image; //按钮图片组件
private Text text; //按钮文本组件
private bool OnOff = false; //是否已展开
private RectTransform rectParent;
private RectTransform childContentRect; //ChildContent对象的RectTransform组件
private GridLayoutGroup gridLayout; //ChildContent对象的GridLayoutGroup组件
private float OldHeight; //展开前高度
private float Height; //展开后高度
public string MenuName;
public Color BtnColorOpen;
public Color TextColorOpen;
public Color BtnColorColse;
public Color TextColorColse;
/// <summary>
/// 初始化
/// </summary>
void Awake()
{
image = transform.GetChild(0).GetComponent<Image>();
text = transform.GetChild(0).GetChild(0).GetComponent<Text>();
rectParent = transform.GetComponent<RectTransform>();
childContentRect = transform.GetChild(2).GetComponent<RectTransform>();
gridLayout = transform.GetChild(2).GetComponent<GridLayoutGroup>();
}
/// <summary>
/// 初始化
/// </summary>
void Start()
{
OldHeight = rectParent.rect.height;
if (childContentRect.childCount > 0)
{
RectTransform rectTransformChild = childContentRect.GetChild(childContentRect.childCount - 1).GetComponent<RectTransform>();
//计算出展开后的高度
Height = (childContentRect.childCount / 2 + childContentRect.childCount % 2) * rectTransformChild.rect.height + gridLayout.padding.top + rectTransformChild.rect.height / 2 + gridLayout.spacing.y;
if (Height < OldHeight)
{
Height = OldHeight;
}
}
}
/// <summary>
/// 设置当前菜单按钮的属性并将二级菜单列表传给子对象中的ChildContent对象
/// </summary>
/// <param name="Menu"></param>
public void SetMenuData(Menus Menu)
{
MenuName = Menu.MenuName;
text.text = MenuName;
gameObject.name = MenuName;
//transform.GetChild(2)为ChildContent对象
childContentData = transform.GetChild(2).GetComponent<ChildContentData>();
childContentData.SetMenuChilds(Menu.MenuChilds);
}
/// <summary>
/// 菜单点击触发按钮
/// </summary>
public void Press()
{
ContentData contentData = transform.parent.GetComponent<ContentData>();
//委托方法
contentData.BtnClick(OnBtnClick, transform);
}
/// <summary>
/// 按钮委托事件
/// </summary>
public void OnBtnClick()
{
OnOff = !OnOff;
if (OnOff)
{
//展开菜单执行的方法。。。。
image.color = BtnColorOpen;
text.color = TextColorOpen;
StartCoroutine(MenuCoroutine(OldHeight));
Debug.Log("点击" + MenuName);
childContentData.SetObjActive(OnOff);
}
else
{
//收缩菜单执行的方法。。。。
image.color = BtnColorColse;
text.color = TextColorColse;
childContentData.RemoveOldTransform(); //关闭二级菜单选中效果
childContentData.SetObjActive(OnOff);
StartCoroutine(MenuCoroutine(Height));
}
}
#region 开关携程(实现伸缩动画效果)
public IEnumerator MenuCoroutine(float nowHeight)
{
while (true)
{
if (OnOff)
{
nowHeight = Mathf.Clamp(nowHeight + Time.deltaTime * 500, OldHeight, Height);
rectParent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, nowHeight);
}
else
{
nowHeight = Mathf.Clamp(nowHeight - Time.deltaTime * 500, OldHeight, Height);
rectParent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, nowHeight);
}
if (nowHeight == Height || nowHeight == OldHeight)
{
break;
}
yield return null;
}
}
#endregion
}
NameImage对象属性
MenuName对象属性
BottonImage属性
这个图片自己整吧,这些属性都可以自己去调整成自己需要的样子
ChildContent对象属性
Grid Layout Group组件需要自己添加,在本例里面这里的约束计数为2代表有两列
创建并挂载ChildContentData.cs脚本文件,脚本内容:
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChildContentData : MonoBehaviour
{
private Transform oldTransform; //上一个点击的按钮
public delegate void BtnClickDelegate();
/// <summary>
/// 二级菜单预制件
/// </summary>
public GameObject childPrefab;
/// <summary>
/// 二级菜单列表
/// </summary>
public List<MenuChilds> MenuChilds;
/// <summary>
/// 添加二级菜单
/// </summary>
/// <param name="menuChilds"></param>
public void SetMenuChilds(List<MenuChilds> menuChilds)
{
MenuChilds = menuChilds;
for (int i = 0; i < MenuChilds.Count; i++)
{
GameObject towMenuBtnObj = Instantiate(childPrefab, transform);
towMenuBtnObj.GetComponent<TowMenuBtn>().SetMenuData(MenuChilds[i]);
}
}
/// <summary>
/// 此处作用为显示或隐藏二级菜单
/// </summary>
/// <param name="active"></param>
public void SetObjActive(bool active)
{
gameObject.SetActive(active);
}
#region 二级菜单点击事件(委托)
public void BtnClick(BtnClickDelegate btnClickDelegate, Transform newTransform)
{
bool equals = false;
if (oldTransform != null)
{
equals = oldTransform.Equals(newTransform) || newTransform == null;
if (!equals || newTransform == null)
{
//关闭上次点击的菜单
oldTransform.GetComponent<TowMenuBtn>().OnBtnClick();
}
}
oldTransform = newTransform;
//执行委托方法,由子对象的TowMenuBtn脚本调用
if (newTransform != null)
{
btnClickDelegate?.Invoke();
if (equals)
{
RemoveOldTransform();
}
}
}
#endregion
#region 关闭菜单
/// <summary>
/// 关闭菜单
/// </summary>
public void RemoveOldTransform()
{
BtnClick(null, null);
}
#endregion
}
五、创建二级菜单预制件,并修改对象组件的属性
二级菜单预制件结构
二级菜单预制件属性
创建并挂载TowMenuBtn.cs脚本文件,脚本内容:
using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TowMenuBtn : MonoBehaviour
{
private bool OnOff = false;//是否点击
private Image image; //按钮图片组件
private Text text; //按钮文本组件
private string TowMenuName; //按钮名称
public MenuChilds MenuChild; //按钮名称
public Color BtnColorOpen;
public Color TextColorOpen;
public Color BtnColorColse;
public Color TextColorColse;
/// <summary>
/// 设置当前菜单按钮的属性
/// </summary>
/// <param name="MenuChild"></param>
public void SetMenuData(MenuChilds MenuChild)
{
if (text == null)
text = transform.GetChild(0).GetComponent<Text>();
if (image == null)
image = transform.GetComponent<Image>();
this.MenuChild = MenuChild;
text.text = MenuChild.MenuChildName;
TowMenuName = MenuChild.MenuChildName;
gameObject.name = MenuChild.MenuChildName;
}
/// <summary>
/// 菜单点击触发按钮
/// </summary>
public void Press()
{
ChildContentData childContentData = transform.parent.GetComponent<ChildContentData>();
//委托方法
childContentData.BtnClick(OnBtnClick, transform);
}
/// <summary>
/// 按钮委托事件
/// </summary>
public void OnBtnClick()
{
OnOff = !OnOff;
if (OnOff)
{
//点击菜单后执行的方法。。。。
image.color = BtnColorOpen;
text.color = TextColorOpen;
Debug.Log("这里是" + TowMenuName);
}
else
{
image.color = BtnColorColse;
text.color = TextColorColse;
}
}
}
Text对象属性
文中一些GameObject的组件属性都可以自行修改成你想要的效果
完,如果本文对你有些帮助,麻烦点个赞支持一下,感激不尽~