Unity self-made multi-level tree menu, realize hierarchical list expansion, imitate unity editing interface Hierarchy window

       If you want to implement a menu hierarchical function, the technical term is called tree menu. Many application software directories use this method. It has a strong sense of hierarchy and a clear structure, but UGUI does not have a native plug-in. I searched the Internet and found related resources. There are not many, and a lot of bugs have been debugged, so I decided to make one according to the Hierarchy style of the unity interface. The directory structure is clear, and submenus can be dynamically added, deleted, and modified. In theory, submenus can be set infinitely.

The template effect is as follows:

Ideas :

Firstly, it is found that the ItemPanel (a single-level menu bar) of each level has the same style, so a panel is needed to hold all the itemPanels.

(ItemPanel)

Add a vertically layout group component to the top panel of the entire menu, so that the submenus below are arranged sequentially from top to bottom. Don’t worry about the arrangement of the content inside, just add itemPanel to it, and a first-level menu directory will be formed.

Then add a second-level menu under the first-level menu, and turn the second-level menu to be added into a sub-object of the first-level menu. The ItemPanel used by the second-level menu is in principle the same as the first-level menu, except that it becomes a first-level menu. child object. The third-level and fourth-level menus are deduced in turn, that is, the sub-objects of the upper level.

(Effective picture) (Edit structure picture)

 

Implementation process:

There are two main classes, one ItemBeanBase and one ItemPanelBase. ItemBeanBase is the base class of an attribute class, which can be assembled into an attribute class according to the data that needs to be filled in a single menu. Then there is ItemPanelBase, which is the base class of a single menu ItemPanel. It needs to mount its subclasses for each submenu ItemPanel. You can customize and inherit the ItemPanelBase class according to your own needs. This class is used to handle the relationship between each ItemPanel and the parent menu and submenus. Logic. Therefore, you can customize the UI style of ItemPanel and its contents according to your own needs.

Given the ItemPanelBase class and the ItemPanel class;

The ItemPanel class (inherited from ItemPanelBase), rewrites the virtual method InitPanelContent() inside, realizes the customization of data (ItemBean), and hangs on each submenu ItemPanel.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemPanelBase : MonoBehaviour
{
    private List<ItemPanelBase> childList;//子物体集合
    [HideInInspector]
    public Button downArrow;//下箭头按钮
    public Sprite down, right,dot;
    public bool isOpen { get; set; }//子物体开启状态
    private Vector2 startSize;//起始大小

    private void Awake()
    {
        childList = new List<ItemPanelBase>();
        downArrow = this.transform.Find("ContentPanel/ArrowButton").GetComponent<Button>();
        downArrow.onClick.AddListener(() =>
        {
            if (isOpen)
            {
                CloseChild();
                isOpen = false;
            }
            else
            {
                OpenChild();
                isOpen = true;
            }
        });
        startSize = this.GetComponent<RectTransform>().sizeDelta;
        isOpen = false;
    }

    //添加子物体到集合
    private void AddChild(ItemPanelBase parentItemPanelBase)
    {
        childList.Add(parentItemPanelBase);
        if (childList.Count >= 1)
        {
            downArrow.GetComponent<Image>().sprite = right;
        }
    }

    /// <summary>
    /// 设置父物体,父物体不为一级菜单
    /// </summary>
    /// <param name="parentItemPanelBase"></param>
    public void SetItemParent(ItemPanelBase parentItemPanelBase)
    {
        this.transform.parent = parentItemPanelBase.transform;
        parentItemPanelBase.AddChild(this);
        this.GetComponent<VerticalLayoutGroup>().padding = new RectOffset((int)parentItemPanelBase.downArrow.GetComponent<RectTransform>().sizeDelta.x, 0, 0, 0);
        if (parentItemPanelBase.isOpen)
        {
            
            this.GetComponent<ItemPanelBase>().AddParentSize((int)this.gameObject.GetComponent<RectTransform>().sizeDelta.y);
        }
        else
        {
            this.transform.gameObject.SetActive(false);        
        }
    }

    /// <summary>
    /// 设置父物体,父物体为一级菜单
    /// </summary>
    /// <param name="tran"></param>
    public void SetBaseParent(Transform tran)
    {
        this.transform.parent = tran;
    }

    /// <summary>
    /// 增加一个子物体后更新Panel大小
    /// </summary>
    /// <param name="change"></param>
    public void UpdateRectTranSize(int change)
    {
        this.gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(startSize.x, this.gameObject.GetComponent<RectTransform>().sizeDelta.y + change);
    }
    /// <summary>
    /// 增加父物体高度
    /// </summary>
    /// <param name="parentItem"></param>
    /// <param name="change"></param>
    public void AddParentSize(int change)
    {
        if (this.transform.parent.GetComponent<ItemPanelBase>() != null)
        {
            this.transform.parent.GetComponent<ItemPanelBase>().UpdateRectTranSize(change);
            this.transform.parent.GetComponent<ItemPanelBase>().AddParentSize(change);
        }
    }

    /// <summary>
    /// 关闭子物体列表
    /// </summary>
    public void CloseChild()
    {
        if (childList.Count == 0) return;
        foreach (ItemPanelBase child in childList)
        {
            child.gameObject.SetActive(false);
            child.GetComponent<ItemPanelBase>().AddParentSize(-(int)child.gameObject.GetComponent<RectTransform>().sizeDelta.y);
        }
        downArrow.GetComponent<Image>().sprite = right;
    }

    /// <summary>
    /// 打开子物体列表
    /// </summary>
    public void OpenChild()
    {
        if (childList.Count == 0) return;
        foreach (ItemPanelBase child in childList)
        {
            child.gameObject.SetActive(true);
            child.GetComponent<ItemPanelBase>().AddParentSize((int)child.gameObject.GetComponent<RectTransform>().sizeDelta.y);
        }
        downArrow.GetComponent<Image>().sprite = down;
    }

    //填充Item数据
    public virtual void InitPanelContent(ItemBeanbase itemBeanbase) { }

}
public class ItemPanel : ItemPanelBase
{

    public override void InitPanelContent(ItemBeanbase itemBeanbase)
    {
        base.InitPanelContent(itemBeanbase);
        ItemBean itemBean = (ItemBean)itemBeanbase;
        this.transform.Find("ContentPanel/Text").GetComponent<Text>().text = itemBean.name + itemBean.age;
    }
}

The following is the calling code of the tree menu:

public class PullDownList : MonoBehaviour
{
    private List<GameObject> itemPanelList;
    public GameObject itemPanel;


    private void Awake()
    {
        itemPanelList = new List<GameObject>();
    }
    // Use this for initialization
    void Start()
    {
        for (int i = 0; i < 10; i++)
        {
            GameObject newItemPanel = Instantiate(itemPanel);
            itemPanelList.Add(newItemPanel);
            newItemPanel.GetComponent<ItemPanelBase>().SetBaseParent(this.transform);
            newItemPanel.GetComponent<ItemPanelBase>().InitPanelContent(new ItemBean("一级菜单" + i, i));
        }

        for (int i = 0; i < 5; i++)
        {
            GameObject newItemPanel2 = Instantiate(itemPanel);
            itemPanelList.Add(newItemPanel2);
            newItemPanel2.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[i].GetComponent<ItemPanelBase>());
            newItemPanel2.GetComponent<ItemPanelBase>().InitPanelContent(new ItemBean("二级菜单" + i, i));
        }

        for (int i = 0; i < 2; i++)
        {
            GameObject newItemPanel3 = Instantiate(itemPanel);
            itemPanelList.Add(newItemPanel3);
            newItemPanel3.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[11].GetComponent<ItemPanelBase>());
            newItemPanel3.GetComponent<ItemPanelBase>().InitPanelContent(new ItemBean("三级菜单" + i, i));
        }
    }

    private void Update()
    {
        if (Input.GetKeyUp(KeyCode.S))
        {
            for (int i = 0; i < 2; i++)
            {
                GameObject newItemPanel4 = Instantiate(itemPanel);
                itemPanelList.Add(newItemPanel4);
                newItemPanel4.GetComponent<ItemPanelBase>().SetItemParent(itemPanelList[1].GetComponent<ItemPanelBase>());
                newItemPanel4.GetComponent<ItemPanelBase>().InitPanelContent(new ItemBean("二级菜单" + i, i));
            }
        }
    }
}

The prefab of itemPanel is needed to generate each submenu, and then create a list to save all submenu items, which is convenient for subsequent operations. You can also dynamically add, delete, and modify menu items. Because the vertical layout group component is used, you only need to modify it The sub-objects inside can realize the dynamic update of the menu view without refresh operation.

The initial rendering of the entire menu is as follows:

The actual effect is on the left, and the structure directory in unity is on the right

The background color of the ItemPanel is not canceled because it is convenient to see the hierarchy between the menus. The comparison chart is given below

(the background color of itemPanel exists, which makes it easy to see the structure)

Different projects have different requirements for the UI, so here is only the logical structure of the tree menu. You can customize the UI or content according to your own needs, and you can modify the style of the ItemPanel prefab according to your needs.

The operation of GIF images is very smooth. . .

The content of the whole project is relatively simple, the structure is clear, the core code is only more than 100 lines, and the scalability is strong. You can modify the functions according to your own needs, so you can directly look at the source code if you don’t understand. The following is the unitypackage (2017.3.1 export ) Baidu cloud disk link, due to the limited level, it is inevitable that there will be mistakes or irregularities, please give me more advice.

Address: https://pan.baidu.com/s/1sCHhK2m3xpYoFTUc4Uiksg

Extraction code: iac9

 

 

Guess you like

Origin blog.csdn.net/qq_33179161/article/details/88973464