UGUI菜单栏伸缩
demo地址,失效了可以找我。。。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ElasticTest : MonoBehaviour
{
public List<GameObject> allObj = new List<GameObject>();//所有的父菜单
Vector2 contentSizedata;//每次更改都需要计算content所容纳的数量
RectTransform contentRt;
GameObject lastObj;//最后一次点击的父菜单
void Start()
{
List<GameObject> child = new List<GameObject>();//
child.Add(GameObject.Find("材料管理 (1)"));
child.Add(GameObject.Find("材料管理 (2)"));
child.Add(GameObject.Find("材料管理 (3)"));
child.Add(GameObject.Find("材料管理 (4)"));
child.Add(GameObject.Find("材料管理 (5)"));
AddItem(GameObject.Find("材料管理"), child);
child.Add(GameObject.Find("材料计划 (1)"));
child.Add(GameObject.Find("材料计划 (2)"));
child.Add(GameObject.Find("材料计划 (3)"));
AddItem(GameObject.Find("材料计划"), child);
child.Add(GameObject.Find("材料入库 (1)"));
child.Add(GameObject.Find("材料入库 (2)"));
AddItem(GameObject.Find("材料入库"), child);
child.Add(GameObject.Find("材料出库 (1)"));
child.Add(GameObject.Find("材料出库 (2)"));
child.Add(GameObject.Find("材料出库 (3)"));
child.Add(GameObject.Find("材料出库 (4)"));
child.Add(GameObject.Find("材料出库 (5)"));
child.Add(GameObject.Find("材料出库 (6)"));
child.Add(GameObject.Find("材料出库 (7)"));
AddItem(GameObject.Find("材料出库"), child);
Init();
}
/// <summary>
/// 初始化
/// </summary>
void Init()
{
lastObj = null;
contentRt = GameObject.Find("Canvas/Scroll View/Viewport/Content").GetComponent<RectTransform>();
contentSizedata = contentRt.sizeDelta;
contentSizedata.y = 0;
for (int i = 0; i < allObj.Count; i++)
{
ChildListState(allObj[i], 0, false);
contentSizedata.y += allObj[i].GetComponent<RectTransform>().sizeDelta.y;
}
contentRt.sizeDelta = contentSizedata;
}
/// <summary>
/// 加入一个父菜单和子菜单
/// </summary>
/// <param name="parent">父菜单物体</param>
/// <param name="childList">子菜单</param>
private void AddItem(GameObject parent, List<GameObject> childList)
{
parent.GetComponent<Button>().onClick.AddListener(delegate { OnClickParent(parent); });
allObj.Add(parent);
ItemGroup group = parent.AddComponent<ItemGroup>();
foreach (GameObject item in childList)
group.childList.Add(item);
childList.Clear();
}
/// <summary>
/// 控制子菜单的显示和隐藏
/// </summary>
/// <param name="item">父菜单物体</param>
/// <param name="isShow">是否显示,0隐藏1显示2列表的第一个物体的相反状态</param>
/// <param name="costSizedata">是否计算sizedata的高度</param>
public void ChildListState(GameObject item, int isShow, bool costSizedata = false)
{
List<GameObject> childlist = item.GetComponent<ItemGroup>().childList;
if (isShow == 2 && childlist.Count > 0)
{
isShow = childlist[0].activeSelf == false ? 1 : 0;
}
for (int i = 0; i < childlist.Count; i++)
{
childlist[i].SetActive(isShow == 1 ? true : false);
if (costSizedata)
{
if (childlist[i].activeSelf)
contentSizedata.y += childlist[i].GetComponent<RectTransform>().sizeDelta.y;
else
contentSizedata.y -= childlist[i].GetComponent<RectTransform>().sizeDelta.y;
}
childlist[i].GetComponentInChildren<Text>().text = item.name + i;
}
contentRt.sizeDelta = contentSizedata;
}
/// <summary>
/// 点击父菜单的时候弹出子菜单
/// </summary>
/// <param name="item">父菜单物体</param>
public void OnClickParent(GameObject item)
{
ChildListState(item, 2, true);
if (lastObj != null)
{
if (lastObj != item)
{
if (lastObj.GetComponent<ItemGroup>().childList[0].activeSelf)
ChildListState(lastObj, 2, true);
}
}
lastObj = item;
}
}
改良版本
**注释已经很详细了,demo也在开头有地址**
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ElasticTest : MonoBehaviour
{
[System.Serializable]
public class ChildList
{
public GameObject parent;
public List<GameObject> child;
}
List<GameObject> allGroup = new List<GameObject>();//所有的父菜单
Vector2 contentSizedata;//每次更改都需要计算content所容纳的数量
RectTransform contentRt;
GameObject lastObj;//最后一次点击的父菜单
Vector2 oriPosition;
public List<ChildList> itemList = new List<ChildList>();
void Start()
{
List<GameObject> child = new List<GameObject>();//
for (int i = 0; i < itemList.Count; i++)
{
AddItem(itemList[i].parent, itemList[i].child);
if (i == 0)
oriPosition = itemList[i].parent.GetComponent<RectTransform>().localPosition;
}
Init();
}
/// <summary>
/// 初始化
/// </summary>
void Init()
{
lastObj = null;
contentRt = GameObject.Find("Canvas/Panel/Scroll View/Viewport/Content").GetComponent<RectTransform>();
for (int i = 0; i < allGroup.Count; i++)
{
ChildListState(allGroup[i], 0);
}
CostContentSize();
}
/// <summary>
/// 加入一个父菜单和子菜单
/// </summary>
/// <param name="parent">父菜单物体</param>
/// <param name="childList">子菜单</param>
private void AddItem(GameObject parent, List<GameObject> childList)
{
parent.GetComponent<Button>().onClick.AddListener(delegate { OnClickParent(parent); });
allGroup.Add(parent);
ItemGroup group = parent.AddComponent<ItemGroup>();
foreach (GameObject item in childList)
group.childList.Add(item);
}
/// <summary>
/// 控制子菜单的显示和隐藏
/// </summary>
/// <param name="item">父菜单物体</param>
/// <param name="isShow">是否显示,0隐藏1显示2列表的第一个物体的相反状态</param>
void ChildListState(GameObject item, int isShow)
{
List<GameObject> childlist = item.GetComponent<ItemGroup>().childList;
if (isShow == 2 && childlist.Count > 0)
{
isShow = childlist[0].activeSelf == false ? 1 : 0;
}
for (int i = 0; i < childlist.Count; i++)
{
childlist[i].SetActive(isShow == 1 ? true : false);
childlist[i].GetComponentInChildren<Text>().text = childlist[i].name;
}
}
List<GameObject> allObj = new List<GameObject>();
List<float> allPos = new List<float>();
float allChildHeight;
bool isCanClick = true;
/// <summary>
/// 点击父菜单的时候弹出子菜单
/// </summary>
/// <param name="item">父菜单物体</param>
void OnClickParent(GameObject item)
{
if (!isCanClick)
{
Debug.Log("点击过快");
return;
}
else
{
Debug.Log("开始");
}
if (lastObj != null)
{
if (lastObj != item)
{
Debug.Log("点击的不是同一个lastObj", lastObj);
Debug.Log("点击的不是同一个item", item);
MoveToTarget(lastObj, false, true);
MoveToTarget(item, true);
}
else
{
if (item.transform.Find("Content").GetChild(0).gameObject.activeSelf)
{
Debug.Log("有一级展开,是自己,收回");
MoveToTarget(item, false);
}
else
{
Debug.Log("有一级展开,是自己,展开");
MoveToTarget(item, true);
}
}
}
else
{
Debug.Log("第一次点击");
MoveToTarget(item, true);
}
lastObj = item;
}
/// <summary>
/// 移动一个列表的物体
/// </summary>
/// <param name="item">父菜单对象</param>
/// <param name="isDown">是否收回</param>
/// <param name="isLast">是否收回子物体</param>
private void MoveToTarget(GameObject item, bool isDown, bool isLast = false)
{
allObj.Clear();//初始化
allPos.Clear();
allChildHeight = 0;//展开的所有子物体的高度
for (int i = 0; i < allGroup.Count; i++)
{
if (item == allGroup[i])//当前点击的对象
{
//新的子物体的Y=初始Y+子物体的索引*子物体的高度+前边所有的子物体的高度(由于是负数所以表达式里省略了+号)
allPos.Add(oriPosition.y - i * allGroup[i].GetComponent<RectTransform>().sizeDelta.y - allChildHeight);
allObj.Add(allGroup[i]);
RectTransform rt = allGroup[i].GetComponent<RectTransform>();
List<GameObject> list = allGroup[i].GetComponent<ItemGroup>().childList;
if (list.Count > 0)//有些菜单可能没有子菜单
{
for (int j = 0; j < list.Count; j++)
{
ReversalGameObject(list[j]);
if (list[j].activeSelf)
{
allChildHeight += list[j].GetComponent<RectTransform>().sizeDelta.y;
allObj.Add(list[j]);
allPos.Add(-list[j].GetComponent<RectTransform>().sizeDelta.y - list[j].GetComponent<RectTransform>().sizeDelta.y * j);
}
}
}
}
else
{
if (isLast)
continue;
RectTransform rt = allGroup[i].GetComponent<RectTransform>();
List<GameObject> list = allGroup[i].GetComponent<ItemGroup>().childList;
if (lastObj == allGroup[i] && list.Count > 0)
{
if (lastObj.GetComponent<ItemGroup>().childList[0].activeSelf)
{
Debug.Log("dd");
for (int j = 0; j < list.Count; j++)
{
ReversalGameObject(list[j], 2);//确保上一个菜单已经关闭
}
}
}
//新的父物体的Y=初始Y+父物体的索引*父物体的高度+前边所有的子物体的高度(由于是负数所以表达式里省略了+号)
allObj.Add(allGroup[i]);
allPos.Add(oriPosition.y - i * allGroup[i].GetComponent<RectTransform>().sizeDelta.y - allChildHeight);
}
}
StartCoroutine(SetObjectsPosition(isDown));//多个协程嵌套,保证以最少的时间完成任务并加强体验
}
void ReversalGameObject(GameObject obj, int state = 0)//0反转1显示 2隐藏
{
if (state == 0)
obj.SetActive(!obj.activeSelf);
else
obj.SetActive(state == 1 ? true : false);
}
private IEnumerator SetObjectsPosition(bool isDown)
{
isCanClick = false;
GameObject[] game = allObj.ToArray();//由于是引用对象,加上协程,有时候协程没执行完却又执行到这里就会出现差错,所以在这里重新创建并赋值
float[] ff = allPos.ToArray();
yield return StartCoroutine(Wait(isDown));
isCanClick = true;
Debug.Log("finish");
CostContentSize();
}
private IEnumerator Wait(bool isDown)
{
GameObject[] game = allObj.ToArray();//由于是引用对象,加上协程,有时候协程没执行完却又执行到这里就会出现差错,所以在这里重新创建并赋值
float[] ff = allPos.ToArray();
for (int i = 0; i < game.Length; i++)
{
if (isDown)
StartCoroutine(GoToPointDownItem(game[i], ff[i]));
else
StartCoroutine(GoToPointUpItem(game[i], ff[i]));
}
yield return null;
}
float shiftY = 20;//渐变的速度
IEnumerator GoToPointDownItem(GameObject currentObj, float target)//弹出
{
RectTransform rt = currentObj.GetComponent<RectTransform>();
Vector2 pos = rt.localPosition;
while (pos.y > target)
{
pos.y -= shiftY;
rt.localPosition = pos;
yield return null;
}
currentObj.GetComponent<RectTransform>().localPosition = new Vector2(currentObj.GetComponent<RectTransform>().localPosition.x, target);
}
IEnumerator GoToPointUpItem(GameObject currentObj, float target)//收回
{
RectTransform rt = currentObj.GetComponent<RectTransform>();
Vector2 pos = rt.localPosition;
while (pos.y < target)
{
pos.y += shiftY;
rt.localPosition = pos;
yield return null;
}
currentObj.GetComponent<RectTransform>().localPosition = new Vector2(currentObj.GetComponent<RectTransform>().localPosition.x, target);
}
void CostContentSize()//计算content的可视范围
{
float allItemHeight = 0;
for (int i = 0; i < allGroup.Count; i++)
{
allItemHeight += allGroup[i].GetComponent<RectTransform>().sizeDelta.y;
List<GameObject> list = allGroup[i].GetComponent<ItemGroup>().childList;
if (list.Count > 0)
{
for (int j = 0; j < list.Count; j++)
{
if (!list[j].activeSelf)
continue;
allItemHeight += list[j].GetComponent<RectTransform>().sizeDelta.y;
}
}
}
float paddingTop = 20;
contentSizedata.y = allItemHeight;
contentSizedata.y += paddingTop;
contentRt.sizeDelta = contentSizedata;
}
}
using System.Collections.Generic;
using UnityEngine;
public class ItemGroup : MonoBehaviour
{
public List<GameObject> childList = new List<GameObject>();
}