Unity Ugui Custom Tree menu (TreeView)

Source: Internet
Author: User

First several:



If that's the effect you need, you're in the right place!


At present, our tree menu shows the following functions:

1, can dynamically configure the data source;

2. Click the context menu button for each element (that is, the triangle button in the diagram) to shrink or expand its child elements;

3, you can independently determine whether the check box of an element is checked, or directly get the current tree menu of all the checked elements;

4. The tree menu uniformly controls the event distribution of all its sub-element buttons;

5, automatic adjustment of the scrolling field of view edge, according to the number of currently visible sub-elements of horizontal and vertical stretching;


First, we first make the template of the child element, that is, a single element of the menu in the diagram, using it to dynamically clone a number of sub-elements based on the data source, here, it is obvious that our template is composed of two button plus a toggle and a text, as follows:


Contextbutton treeviewtoggle Treeviewbutton (treeviewtext)



The text in the diagram is a textbox that describes the name or content of this element, which corresponds to the structure:




Each of our child elements will carry a TreeViewItem script that describes itself as a parent-child relationship to other elements throughout the tree menu, and the entire tree menu is controlled by TreeviewControl, first, TreeviewControl will generate all of the child elements based on the provided data source, and of course, it is also the same method to regenerate after changing the data source, and the simple thing is to create the elements with templates and establish a parent-child relationship:

<summary>///Generate tree Menu///</summary> public void Generatetreeview () {//delete a tree menu element that may already exist                if (_treeviewitems! = null) {for (int i = 0; i < _treeviewitems.count; i++) {            Destroy (_treeviewitems[i]);        } _treeviewitems.clear ();        }//re-create the tree menu element _treeviewitems = new list<gameobject> ();            for (int i = 0; i < Data.count; i++) {Gameobject item = instantiate (Template); if (Data[i]. ParentID = =-1) {item. Getcomponent<treeviewitem> ().                Sethierarchy (0); Item. Getcomponent<treeviewitem> ().            SetParent (NULL); } else {TreeViewItem TVI = _treeviewitems[data[i]. ParentID].                Getcomponent<treeviewitem> (); Item. Getcomponent<treeviewitem> (). Sethierarchy (TVI.                GetHierarchy () + 1); Item. Getcomponent<treeviewItem> ().                SetParent (TVI); TVi. Addchildren (item.            Getcomponent<treeviewitem> ());            } item.transform.name = "TreeViewItem"; Item.transform.FindChild ("Treeviewtext"). Getcomponent<text> (). Text = Data[i].            Name;            Item.transform.SetParent (Treeitems);            Item.transform.localPosition = Vector3.zero;            Item.transform.localScale = Vector3.one;            Item.transform.localRotation = Quaternion.euler (Vector3.zero); Item.            SetActive (TRUE);        _treeviewitems.add (item); }    }


After the tree menu is generated, all elements, although they record the parent-child relationship with other elements, But their position is in Vector3.zero, after all, our menu elements in the creation of the time are peremptorily dropped to the origin of the location, the creation of June can be no matter how many elements squeezed in a heap will not suppress death, OK, after the orderly procession of things to the refreshed June to complete, refresh June play a good hand recursion, it will traverse all elements and eliminate See the elements (that is, click the triangle button to hide) and rearrange them one after the other, after the parent, after the son, and so on ... It iterates through the list of child elements of each element, finds that child elements are visible and enters the list of child elements, and discovers that the grandchild element is visible and enters the list of grandchild elements:

<summary>///Refresh Tree menu///</summary> public void Refreshtreeview () {_yindex = 0;        _hierarchy = 0;        Copy a menu _treeviewitemsclone = new list<gameobject> (_treeviewitems);            Refresh calculations with the copied menu for (int i = 0; i < _treeviewitemsclone.count; i++) {//elements that have been calculated or do not need to be calculated            if (_treeviewitemsclone[i] = = NULL | |!_treeviewitemsclone[i].activeself) {continue; } TreeViewItem TVI = _treeviewitemsclone[i].            Getcomponent<treeviewitem> (); _treeviewitemsclone[i]. Getcomponent<recttransform> (). localposition = new Vector3 (TVI.            GetHierarchy () * horizontalitemspace, _yindex,0);            _yindex + = (-(itemheight + verticalitemspace)); if (TVI. GetHierarchy () > _hierarchy) {_hierarchy = TVi.            GetHierarchy (); }//If the child element is expanded, continue to flush the IF (TVI) down.             isexpanding) {   Refreshtreeviewchild (TVI);        } _treeviewitemsclone[i] = null;        }//Recalculate the area of the scrolling field float x = _hierarchy * horizontalitemspace + itemwidth;        Float y = mathf.abs (_yindex); Transform.        Getcomponent<scrollrect> (). Content.sizedelta = new Vector2 (x, y);    Empty the copied menu _treeviewitemsclone.clear ();        }///<summary>//Refresh all child elements of the element///</summary> void Refreshtreeviewchild (TreeViewItem tvi) { for (int i = 0; i < TVI. Getchildrennumber (); i++) {TVI. Getchildrenbyindex (i) .gameobject.getcomponent<recttransform> (). localposition = new Vector3 (TVI. Getchildrenbyindex (i).            GetHierarchy () * horizontalitemspace, _yindex, 0);            _yindex + = (-(itemheight + verticalitemspace)); if (TVI. Getchildrenbyindex (i). GetHierarchy () > _hierarchy) {_hierarchy = TVi. Getchildrenbyindex (i).            GetHierarchy ();      }//If the child element is expanded, continue to refresh down      if (TVI. Getchildrenbyindex (i). isexpanding) {refreshtreeviewchild (TVI).            Getchildrenbyindex (i)); } int index = _treeviewitemsclone.indexof (TVI.            Getchildrenbyindex (i). Gameobject);            if (index >= 0) {_treeviewitemsclone[index] = null; }        }    }

I have copied all the elements here to calculate the position, primarily to prevent an element from being accessed two or more times during a round of refreshes, since the refresh will traverse all visible elements if the first access to element a (where element a is refreshed) After accessing the element B (where element B is refreshed) based on the child element list of element A, when the bottom of the child element is reached, when there is no deeper child element, the element returned to element a continues to access, when element B may be after element A in the list of all elements. That is, element B has been accessed through the parent element, does not need to do the re-visit, his position is the latest, and then according to the list index is likely to revisit the element B, if so, the position of element B is also refreshed once, or even multiple times, the performance impact does not say, The position of the second calculation is no longer the correct position.


Four, the menu has been created and after a round of refresh, at this time it is shown is such a shape of all the child elements are expanded (I specify the data source in the demo, about the data source is set in the following):




We want to listen to the context button of the triangle in the script TreeViewItem that each element carries, and when the mouse clicks on it, its child elements are collapsed or expanded:

<summary>//////Click the context menu button, the element's child element changes the display state///</summary> void Contextbuttonclick () {if (IsE xpanding) {transform. Findchild ("Contextbutton").            Getcomponent<recttransform> (). Localrotation = Quaternion.euler (0, 0, 90);            Isexpanding = false;        Changechildren (this, false); } else {transform. Findchild ("Contextbutton").            Getcomponent<recttransform> (). Localrotation = Quaternion.euler (0, 0, 0);            Isexpanding = true;        Changechildren (this, true);    }//Refresh Tree menu Controler.refreshtreeview (); }///<summary>///change the display status of all child elements of an element///</summary> void Changechildren (TreeViewItem TVI, bool Val UE) {for (int i = 0; i < TVI. Getchildrennumber (); i++) {TVI.            Getchildrenbyindex (i). gameobject.setactive (value); Changechildren (TVI.        Getchildrenbyindex (i), value); }    }

Isexpanding is used as a field for each element to set or read the display state of its own child elements, where all child elements of this element and the grandchild elements are recursively looped, so that they are visible or hidden, depending on the state of the change.


Five, to all the child elements of the unified event distribution, here is the main mouse click this event:


Each element will register this event: (TreeViewItem.cs)

void Awake ()    {        //Context button click Callback        transform. Findchild ("Contextbutton"). Getcomponent<button> (). Onclick.addlistener (Contextbuttonclick);        Transform. Findchild ("Treeviewbutton"). Getcomponent<button> (). Onclick.addlistener (Delegate () {            controler.clickitem (gameobject);        });    
Tree Menu Controller Unified Distribution: (TreeViewControl.cs)

public delegate void Clickitemdelegate (Gameobject item);    Public event Clickitemdelegate clickitemevent;///<summary>///    mouse click child element Events///    </summary>    public void Clickitem (Gameobject item)    {        clickitemevent (item);    }


Six, get the check box state of the element to determine whether it is checked:


Filter by element name to get the selected state of this element, which may not be good if there is an element with the same name:

<summary>////    Returns whether the child element of the specified name is checked    ///</summary> public bool Itemischeck (string itemname)    {for        (int i = 0; i < _treeviewitems.count; i++)        {            if (_treeviewitems[i].transform. Findchild ("Treeviewtext"). Getcomponent<text> (). Text = = ItemName)            {                return _treeviewitems[i].transform. Findchild ("Treeviewtoggle"). Getcomponent<toggle> (). isOn;            }        }        return false;    }

Returns a collection of all the checked child element names in the tree menu:

<summary>    ///Return all child element names that are checked in the tree menu    ////</summary> public list<string> Itemsischeck (    {        list<string> items = new list<string> ();        for (int i = 0; i < _treeviewitems.count; i++)        {            if (_treeviewitems[i].transform. Findchild ("Treeviewtoggle"). Getcomponent<toggle> (). IsOn)            {                items. ADD (_treeviewitems[i].transform. Findchild ("Treeviewtext"). Getcomponent<text> (). Text);            }        }        return items;    }



Seven, next is our data format treeviewdata, the data source for the tree menu is a collection of this format:


<summary>///    data Source for current tree menu///    </summary>    [hideininspector] public    list< Treeviewdata> Data = null;

Each treeviewdata represents an element, name is the text content displayed, ParentID the index of the parent element it points to in the entire data set, starting from 0, 1 means that there is no root element for the parent element, and of course sometimes the data source is not the same, possibly XML, It could be JSON, but it can be changed after the data source is parsed:

<summary>///Tree Menu Data//</summary>public class treeviewdata{///<summary>//    data content    </summary> public    string Name;    <summary>///    The parent ID of the data///    </summary> public    int parentid;}


Viii. Properties Panel Parameters:


Template: element templates for the current tree menu;

Treeitems: The element root object of the current tree menu, automatically assigned, this does not move;

Verticalitemspace: The longitudinal spacing between adjacent elements;

Horizontalitemspace: Horizontal spacing between different levels of elements;

Itemwidth: The width of the element, if you modify the template, the value here needs to calculate the approximate width of the template;

ItemHeight: The height of the element, if you modify the template itself, the value here to calculate the approximate height of the template;



Nine, I've packaged the TreeView into a plug-in, and imported him in unity, you can use the TreeView directly:


After importing Treeview.unitypackage, create a canvas (canvas) in the scene and right-click to create the TreeView directly:




Then get the TreeView in the other script and directly specify the data source for him (I'm building it manually and it's a bit long):

Generate Data list<treeviewdata> datas = new list<treeviewdata> ();        Treeviewdata data = new Treeviewdata (); Data.        Name = "first chapter"; Data.        ParentID =-1; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1. First section"; Data.        ParentID = 0; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1. Section II"; Data.        ParentID = 0; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1. First lesson"; Data.        ParentID = 1; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.2. First lesson"; Data.        ParentID = 2; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1. Lesson two"; Data.        ParentID = 1; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1.1. First article"; Data.        ParentID = 3; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1.1. Second article"; Data.        ParentID = 3; Datas. ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1.1.2. First paragraph"; Data.        ParentID = 7; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1.1.2. Second paragraph"; Data.        ParentID = 7; Datas.        ADD (data);        data = new Treeviewdata (); Data.        Name = "1.1.1.2.1. First question"; Data.        ParentID = 8; Datas.        ADD (data); Specifies the data source treeview.data = datas;

A tree menu is then generated and refreshed once:

Regenerate Tree menu        treeview.generatetreeview ();        Refresh Tree Menu        treeview.refreshtreeview ();

Then register the child element's mouse click event (the delegate type is the return value void, with a gameobject type parameter, the parameter item is the gameobject of that element in the mouse point):

Register child element Mouse Click event        treeview.clickitemevent + = callback;void CallBack (gameobject Item)    {        Debug.Log ("clicked" + Item.transform.FindChild ("Treeviewtext"). Getcomponent<text> (). Text);    }

And to get the tick status of an element:

BOOL Ischeck = Treeview.itemischeck ("first chapter");            Debug.Log ("Elements in the current tree menu chapter I" + (Ischeck? ") Has been selected! ":" Is not selected! "));

And get all the checked elements:

list<string> items = Treeview.itemsischeck ();            for (int i = 0; i < items. Count; i++)            {                Debug.Log (the element selected in the current tree menu is: "+ items[i]);            }



As follows:



Plugin Link: has been uploaded, and so on the post-approval link

Unity Ugui Custom Tree menu (TreeView)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.