Unity编辑器开辟 Immediate Mode GUI (IMGUI)

打印 上一主题 下一主题

主题 1019|帖子 1019|积分 3057

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 简介:

IMGUI是代码驱动gui系统,由 OnGUI 函数驱动:
  1. void OnGUI() {
  2.     if (GUILayout.Button("Press Me")){
  3.         Debug.Log("Hello!");
  4.     }
  5. }
复制代码
IMGUI常用于:
        创建 in-game debugging displays and tools;
        为组件创建自定义inspector;
        创建editor windows and tools;
2. IMGUI底子:

OnGUI()函数在脚本激活时每帧调用;
  1. using UnityEngine;
  2. [ExecuteAlways]
  3. public class TestGUI : MonoBehaviour
  4. {
  5.     private void OnGUI()
  6.     {
  7.         GUI.Box(new Rect(10, 10, 100, 90), "Loader Menu");
  8.         if(GUI.Button(new Rect(20, 40, 80, 20), "Button 1"))
  9.         {
  10.             Debug.Log("Button 1");
  11.         }
  12.         if (GUI.Button(new Rect(20, 70, 80, 20), "Button 2"))
  13.         {
  14.             Debug.Log("Button 2");
  15.         }
  16.     }
  17. }
复制代码
        

3. 调用函数来绘制控件:
        (GUI/GUILayout).Type (Rect, Content);
定义 Rect 时,坐标系基于屏幕左上角,可使用 Screen.width 和 Screen.height ;
Content范例:
        string;
        Texture2D;
        GUIContent;
  1. GUI.Box(new Rect(10, 10, 100, 50), icon);
  2. GUI.Box(new Rect(10, 60, 100, 50), new GUIContent("text", icon));
  3. GUI.Box(new Rect(10, 110, 100, 50), new GUIContent("text", icon, "This is the tooltip"));
  4. GUI.Button(new Rect(10, 160, 100, 20), new GUIContent("Click me", "This is the tooltip"));
  5. GUI.Label(new Rect(10, 180, 100, 20), GUI.tooltip); //显示当前的tooltip内容
复制代码
        

3. IMGUI控件:

Label:

  1. GUI.Label(new Rect(100, 100, 100, 30), "show text");
复制代码
Button:

  1. if(GUI.Button(new Rect(100, 100, 100, 30), "button"))
  2. {
  3.     Debug.Log("Click button");
  4. }
复制代码
RepeatButton:

        类似 Button,只是点击时一直返回 true;
TextField:

  1. str = GUI.TextField(new Rect(100, 100, 100, 30), str);
复制代码
        单行文本输入;
TextArea:

  1. str = GUI.TextArea(new Rect(100, 100, 100, 100), str);
复制代码
        多行文本输入;
Toggle:

  1. toggleVal = GUI.Toggle(new Rect(100, 100, 100, 30), toggleVal, "Toggle");
复制代码
Toolbar:

        一行单选按钮,toolbarInt 用来记录选择的按钮索引;
  1. private int toolbarInt = 0;
  2. private string[] toolbarStrings = { "Toolbar1", "Toolbar2", "Toolbar3" };
  3. void OnGUI()
  4. {
  5.     toolbarInt = GUI.Toolbar(new Rect(100, 100, 300, 30), toolbarInt, toolbarStrings);
  6. }
复制代码
SelectionGrid:

        多行Toolbar;
  1. private int selectionIndex = 0;
  2. private string[] selectionStrings = { "Toolbar1", "Toolbar2", "Toolbar3", "Toolbar4" };
  3. void OnGUI()
  4. {
  5.     selectionIndex = GUI.SelectionGrid(new Rect(100, 100, 300, 60), selectionIndex, selectionStrings, 2);
  6. }
复制代码
HorizontalSlider:

        水平滑动条
  1. private float sliderValue = 0.0f;
  2. void OnGUI()
  3. {
  4.     sliderValue = GUI.HorizontalSlider(new Rect(100, 100, 100, 30), sliderValue, 0.0f, 10.0f);
  5. }
复制代码
VerticalSlider:

        垂直滑动条;
  1. private float sliderValue = 0.0f;
  2. void OnGUI()
  3. {
  4.     sliderValue = GUI.VerticalSlider(new Rect(100, 100, 100, 30), sliderValue, 0.0f, 10.0f);
  5. }
复制代码
HorizontalScrollbar:

        类似 Slider,用于导航 ScrollView 控件,有一个额外参数控制旋钮宽度;
  1. private float scrollValue = 0.0f;
  2. void OnGUI()
  3. {
  4.     scrollValue = GUI.HorizontalScrollbar(new Rect(100, 100, 100, 30), scrollValue, 1, 0.0f, 10.0f);
  5. }
复制代码
VerticalScrollbar:

        类似 HorizontalScrollbar;
ScrollView:

  1. private Vector2 scrollPosition = Vector2.zero;
  2. void OnGUI()
  3. {
  4.     //第一个参数指定可视区域,第三个参数指定在滚动列表内的大小
  5.     scrollPosition = GUI.BeginScrollView(new Rect(100, 100, 100, 100), scrollPosition, new Rect(0, 0, 400, 400));
  6.     GUI.Label(new Rect(0, 0, 400, 400), "text");
  7.     GUI.EndScrollView();
  8. }
复制代码
Window:

        可拖动的容器;
  1. private Rect windowRect = new Rect(100, 100, 100, 100);
  2. void OnGUI()
  3. {
  4.     windowRect = GUI.Window(0, windowRect, WindowFunction, "My Window");
  5.    
  6. }
  7. void WindowFunction(int id)
  8. {
  9.     GUI.Label(new Rect(10, 20, 100, 30), "text");
  10.     GUI.DragWindow();   //让window可拖动
  11. }
复制代码
GUI.changed:
        检测用户是否有操作;
  1. selectIndex = GUI.Toolbar(new Rect(100, 100, 200, 30), selectIndex, options);
  2. if(GUI.changed)
  3. {
  4.     Debug.Log("selectIndex = " + selectIndex);
  5. }
复制代码
4. 使用GUIStyles样式化控件:

GUIStyles 控制控件的外观显示;
        

GUISkins 包罗一系列 GUIStyles;
        

所有控件可设置一个可选的 GUIStyle;
  1. //指定 GUIStyle 的名字 button
  2. GUI.Label(new Rect(100, 100, 100, 30), "text", "button");
复制代码
        

在脚本中声明一个序列化的GUIStyle ,查看属性:
  1. public GUIStyle style;
  2. void OnGUI()
  3. {
  4.     //指定 GUIStyle 的名字 button
  5.     GUI.Label(new Rect(100, 100, 100, 30), "text", style);
  6. }
复制代码
        

设置GUISkin:
  1. public GUISkin skin;
  2. void OnGUI()
  3. {
  4.     GUI.skin = skin;
  5.     //绘制
  6.     //...
  7.     //恢复默认skin
  8.     GUI.skin = null;    //
  9. }
复制代码
改变GUIStyle:
  1. GUIStyle style = GUI.skin.GetStyle("label");
  2. style.fontSize = 30;
  3. GUI.Label(new Rect(100, 100, 400, 100), "text1");
  4. style.fontSize = 50;
  5. GUI.Label(new Rect(100, 150, 400, 100), "text2");
复制代码
5. IMGUI Layout 模式:

固定结构和自动结构:

要使用自动结构,使用 GUILayout 代替GUI;
可以在 OnGUI() 函数中同时使用两种结构方式,但是两种结构相互独立;
GUILayout 不必要指定 Rect;
  1. // Fixed Layout
  2. GUI.Button(new Rect(25, 25, 100, 30), "I am a Fixed Layout Button");
  3. // Automatic Layout
  4. GUILayout.Button("I am an Automatic Layout Button");
复制代码
固定结构 Group:

        Group内的控件将基于组的左上角定位;
  1. GUI.BeginGroup(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 50, 100, 100));
  2. GUI.Box(new Rect(0, 0, 100, 100), "Group is here");
  3. GUI.Button(new Rect(10, 40, 80, 30), "Click me");
  4. GUI.EndGroup();
复制代码
可以嵌套使用Group,可获取裁剪效果:
  1. // background image that is 256 x 32
  2. public Texture2D bgImage;
  3. // foreground image that is 256 x 32
  4. public Texture2D fgImage;
  5. // a float between 0.0 and 1.0
  6. public float playerEnergy = 1.0f;
  7. //使用默认style绘制box
  8. public GUIStyle style = new GUIStyle();
  9. void OnGUI()
  10. {
  11.     if(bgImage == null || fgImage == null)
  12.     {
  13.         return;
  14.     }
  15.     GUI.BeginGroup(new Rect(300, 300, 256, 32));
  16.     // 绘制背景
  17.     GUI.Box(new Rect(0, 0, 256, 32), bgImage, style);
  18.     // 通过 playerEnergy 控制显示大小,达到裁剪效果
  19.     GUI.BeginGroup(new Rect(0, 0, playerEnergy * 256, 32));
  20.     GUI.Box(new Rect(0, 0, 256, 32), fgImage, style);
  21.     GUI.EndGroup();
  22.     GUI.EndGroup();
  23. }
复制代码
        

自动结构 Area:

类似固定结构 Group;
定义屏幕的一块区域来包罗 GUILayout  控件,控件基于 Area 的左上角定位;
  1. GUILayout.Button("I am not inside an Area");
  2. GUILayout.BeginArea(new Rect(Screen.width / 2, Screen.height / 2, 300, 300));
  3. GUILayout.Button("I am completely inside an Area");
  4. GUILayout.EndArea();
复制代码
注意Area里控件的宽度将被拉伸到与Area一样;
自动结构 Horizontal and Vertical Group:

使用自动结构时,控件默认从上往下一个接一个;
为了修改这一行为,提供了Horizontal 和 Vertical Group;
Horizontal Group 里的控件被水平摆放;
        GUILayout.BeginHorizontal()GUILayout.EndHorizontal()
Vertical Group 里的控件被垂直摆放;
        GUILayout.BeginVertical(), and GUILayout.EndVertical()
  1. GUILayout.BeginArea(new Rect(400, 400, 200, 100));
  2. GUILayout.BeginHorizontal();
  3. GUILayout.Button("Button\nfirst");
  4. GUILayout.BeginVertical();
  5. GUILayout.Button("Button2");
  6. GUILayout.Label("Label");
  7. GUILayout.EndVertical();
  8. GUILayout.EndHorizontal();
  9. GUILayout.EndArea();
复制代码
        

GUILayoutOptions :

        使用 GUILayoutOptions 覆盖一些自动结构参数;
  1. GUILayout.BeginArea(new Rect(100, 50, Screen.width - 200, Screen.height - 100));
  2. GUILayout.Button("I am a regular Automatic Layout Button");
  3. GUILayout.Button("My width has been overridden", GUILayout.Width(95));
  4. GUILayout.EndArea();
复制代码
        

6. 使用 IMGUI 扩展编辑器:

1. 创建自定义 Editor Window:

1. 创建脚本继续 EditorWindow:
  1. using UnityEditor;
  2. using UnityEngine;
  3. public class MyWindow : EditorWindow
  4. {
  5.     private void OnGUI()
  6.     {
  7.         
  8.     }
  9. }
复制代码
2. 显示window:
  1. using UnityEditor;
  2. using UnityEngine;
  3. public class MyWindow : EditorWindow
  4. {
  5.     [MenuItem("Window/My Window")]
  6.     public static void ShowWindow()
  7.     {
  8.         EditorWindow.GetWindow(typeof(MyWindow));
  9.     }
  10.     private void OnGUI()
  11.     {
  12.         
  13.     }
  14. }
复制代码
3. 实现 gui:
EditorGUI 和  EditorGUILayout 提供了额外的控件;
  1. using UnityEditor;
  2. using UnityEngine;
  3. public class MyWindow : EditorWindow
  4. {
  5.     string myString = "Hello World";
  6.     bool groupEnabled;
  7.     bool myBool = true;
  8.     float myFloat = 1.23f;
  9.     [MenuItem("Window/My Window")]
  10.     public static void ShowWindow()
  11.     {
  12.         EditorWindow.GetWindow(typeof(MyWindow));
  13.     }
  14.     private void OnGUI()
  15.     {
  16.         GUILayout.Label("Base Settings", EditorStyles.boldLabel);
  17.         myString = EditorGUILayout.TextField("Text Field", myString);
  18.         groupEnabled = EditorGUILayout.BeginToggleGroup("Optional Settings", groupEnabled);
  19.         myBool = EditorGUILayout.Toggle("Toggle", myBool);
  20.         myFloat = EditorGUILayout.Slider("Slider", myFloat, -3, 3);
  21.         EditorGUILayout.EndToggleGroup();
  22.     }
  23. }
复制代码
2. 自定义 Inspector:

1. 自定义 GUI of Serializable class:

  1. using System;
  2. using UnityEngine;
  3. public enum PersonLevel { A, B, C}
  4. [Serializable]
  5. public class Person
  6. {
  7.     public string name;
  8.     public int age;
  9.     public PersonLevel personLevel;
  10. }
  11. public class PersonInfo : MonoBehaviour
  12. {
  13.     public Person person;
  14.     public Person[] persons;
  15. }
复制代码
        

        (默认显示)
使用 Property Drawer 来控制其在 Inspector 上的显示:
  1. using UnityEditor;
  2. using UnityEngine;
  3. [CustomPropertyDrawer(typeof(Person))]
  4. public class PersonDrawer : PropertyDrawer
  5. {
  6.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  7.     {
  8.         // Using BeginProperty / EndProperty on the parent property means that
  9.         // prefab override logic works on the entire property.
  10.         EditorGUI.BeginProperty(position, label, property);
  11.         // Draw label
  12.         position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
  13.         // Don't make child fields be indented
  14.         var indent = EditorGUI.indentLevel;
  15.         EditorGUI.indentLevel = 0;
  16.         // Calculate rects
  17.         var nameRect = new Rect(position.x, position.y, 30, position.height);
  18.         var ageRect = new Rect(position.x + 35, position.y, 50, position.height);
  19.         var personLevelRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);
  20.         // Draw fields - pass GUIContent.none to each so they are drawn without labels
  21.         EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
  22.         EditorGUI.PropertyField(ageRect, property.FindPropertyRelative("age"), GUIContent.none);
  23.         EditorGUI.PropertyField(personLevelRect, property.FindPropertyRelative("personLevel"), GUIContent.none);
  24.         // Set indent back to what it was
  25.         EditorGUI.indentLevel = indent;
  26.         EditorGUI.EndProperty();
  27.     }
  28. }
复制代码
2. 使用 Property Attribute 自定义脚本成员 GUI:

创建 Attribute:
  1. using UnityEngine;
  2. public class MyRangeAttribute : PropertyAttribute
  3. {
  4.     public float min;
  5.     public float max;
  6.     public MyRangeAttribute(float min, float max)
  7.     {
  8.         this.min = min;
  9.         this.max = max;
  10.     }
  11. }
复制代码
对 Attribute 进行自定义绘制:
  1. using UnityEditor;
  2. using UnityEngine;
  3. //对MyRangeAttribute
  4. [CustomPropertyDrawer(typeof(MyRangeAttribute))]
  5. public class RangeDrawer : PropertyDrawer
  6. {
  7.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  8.     {
  9.         // First get the attribute since it contains the range for the slider
  10.         MyRangeAttribute range = (MyRangeAttribute)attribute;
  11.         // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
  12.         if (property.propertyType == SerializedPropertyType.Float)
  13.             EditorGUI.Slider(position, property, range.min, range.max, label);
  14.         else if (property.propertyType == SerializedPropertyType.Integer)
  15.             EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label);
  16.         else
  17.             EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
  18.     }
  19. }
复制代码
        (注意,为了性能考虑,EditorGUILayout 不可在 PropertyDrawer 中使用)
使用 Attribute:
  1. using UnityEngine;
  2. public class TestAttribute : MonoBehaviour
  3. {
  4.     [MyRange(1.5f, 35f)]
  5.     public float fValue;
  6. }
复制代码
        

3. Component 自定义编辑器:

创建 component:
  1. using UnityEngine;
  2. [ExecuteInEditMode]
  3. public class LookAtPoint : MonoBehaviour
  4. {
  5.     public Vector3 lookAtPoint = Vector3.zero;
  6.     void Update()
  7.     {
  8.         transform.LookAt(lookAtPoint);
  9.     }
  10. }
复制代码
创建组件自定义编辑器:
  1. using UnityEditor;
  2. [CustomEditor(typeof(LookAtPoint))]
  3. [CanEditMultipleObjects]
  4. public class LookAtPointEditor : Editor
  5. {
  6.     LookAtPoint targetComp;
  7.     SerializedProperty lookAtPoint;
  8.     void OnEnable()
  9.     {
  10.         lookAtPoint = serializedObject.FindProperty("lookAtPoint");
  11.     }
  12.     public override void OnInspectorGUI()
  13.     {
  14.         targetComp = target as LookAtPoint;
  15.         serializedObject.Update();
  16.         EditorGUILayout.PropertyField(lookAtPoint);
  17.         serializedObject.ApplyModifiedProperties();
  18.     }
  19. }
复制代码
通过 OnSceneGUI 往 Scene View 中添加 编辑器代码:
  1. using UnityEngine;
  2. using UnityEditor;
  3. [CustomEditor(typeof(LookAtPoint))]
  4. [CanEditMultipleObjects]
  5. public class LookAtPointEditor : Editor
  6. {
  7.     private void OnSceneGUI()
  8.     {
  9.         LookAtPoint t = (LookAtPoint)target;
  10.         //使用 Handles 类来实现3D场景编辑控制
  11.         EditorGUI.BeginChangeCheck();
  12.         //给定位置,在场景中画中控制轴,拖动的话,返回变化的pos
  13.         Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
  14.         if (EditorGUI.EndChangeCheck())
  15.         {
  16.             Undo.RecordObject(target, "Move point");
  17.             t.lookAtPoint = pos;
  18.             t.Update();
  19.         }
  20.     }
  21. }
复制代码
如果要在场景中添加 GUI,使用 Handles.BeginGUI() and Handles.EndGUI();
4. TreeView :

是一个 IMGUI 控件,用于显示分层数据,可展开和折叠;
使用 TreeView 创建高度自定义的列表视图和多列表格;
按行渲染,每行代表一个 TreeViewItem, 包罗父节点和子节点的信息;
TreeView 有一个潜伏的根 TreeViewItem,是所有其他 item的根;
重要的类和方法:

类 TreeViewState:
        包罗交互的状态信息,可序列化,以便在编辑器编译和进入Play模式时生存数据;
类 TreeViewItem:
        包罗每个 TreeView item 的数据,在 TreeView 中必要一个唯一 id;
        depth 属性用来视觉缩进;
抽象方法 BuildRoot:
        TreeView 类的抽象方法,每次调用 TreeView 的 Reload 方法就会触发;
        用于创建 root item;
        对于简单树,可创建所有的 TreeViewItem;
        对于复杂树,通过覆写 BuildRows 来创建当前必要的行;
虚方法 BuildRows:
        默认实现创建整个树;
        可覆写只创建展开的行;

初始化 TreeView:

调用 Reload 方法来初始化;
两种方式设置 TreeView:
1. 在 BuildRoot 中创建整个树;
2. 在 BuildRoot  中只创建根,然后在 BuildRows 中只创建展开的 items;
Example 1: A simple TreeView:

  1. using System.Collections.Generic;
  2. using UnityEditor.IMGUI.Controls;
  3. public class SimpleTreeView : TreeView
  4. {
  5.     public SimpleTreeView(TreeViewState state) : base(state)
  6.     {
  7.         Reload();
  8.     }
  9.     protected override TreeViewItem BuildRoot()
  10.     {
  11.         // BuildRoot is called every time Reload is called to ensure that TreeViewItems
  12.         // are created from data. Here we create a fixed set of items. In a real world example,
  13.         // a data model should be passed into the TreeView and the items created from the model.
  14.         // This section illustrates that IDs should be unique. The root item is required to
  15.         // have a depth of -1, and the rest of the items increment from that.
  16.         var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
  17.         var allItems = new List<TreeViewItem>
  18.         {
  19.             new TreeViewItem {id = 1, depth = 0, displayName = "Animals"},
  20.             new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"},
  21.             new TreeViewItem {id = 3, depth = 2, displayName = "Tiger"},
  22.             new TreeViewItem {id = 4, depth = 2, displayName = "Elephant"},
  23.             new TreeViewItem {id = 5, depth = 2, displayName = "Okapi"},
  24.             new TreeViewItem {id = 6, depth = 2, displayName = "Armadillo"},
  25.             new TreeViewItem {id = 7, depth = 1, displayName = "Reptiles"},
  26.             new TreeViewItem {id = 8, depth = 2, displayName = "Crocodile"},
  27.             new TreeViewItem {id = 9, depth = 2, displayName = "Lizard"},
  28.         };
  29.         // Utility method that initializes the TreeViewItem.children and .parent for all items.
  30.         SetupParentsAndChildrenFromDepths(root, allItems);
  31.         // Return root of the tree
  32.         return root;
  33.     }
  34. }
复制代码
另一种方式添加 Item:
  1. protected override TreeViewItem BuildRoot()
  2. {
  3.     // BuildRoot is called every time Reload is called to ensure that TreeViewItems
  4.     // are created from data. Here we create a fixed set of items. In a real world example,
  5.     // a data model should be passed into the TreeView and the items created from the model.
  6.     // This section illustrates that IDs should be unique. The root item is required to
  7.     // have a depth of -1, and the rest of the items increment from that.
  8.     var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
  9.     //不指定depth,通过 TreeViewItem.AddChild 来添加节点
  10.     var animals = new TreeViewItem { id = 1, displayName = "Animals" };
  11.     root.AddChild(animals);
  12.     var mammals = new TreeViewItem { id = 2, displayName = "Mammals" };
  13.     animals.AddChild(mammals);
  14.     mammals.AddChild(new TreeViewItem { id = 3, displayName = "Tiger" });
  15.     mammals.AddChild(new TreeViewItem { id = 4, displayName = "Elephant" });
  16.     mammals.AddChild(new TreeViewItem { id = 5, displayName = "Okapi" });
  17.     mammals.AddChild(new TreeViewItem { id = 6, displayName = "Armadillo" });
  18.     var reptiles = new TreeViewItem { id = 7, displayName = "Reptiles" };
  19.     animals.AddChild(reptiles);
  20.     reptiles.AddChild(new TreeViewItem { id = 8, displayName = "Crocodile" });
  21.     reptiles.AddChild(new TreeViewItem { id = 9, displayName = "Lizard" });
  22.     SetupDepthsFromParentsAndChildren(root);
  23.     // Return root of the tree
  24.     return root;
  25. }
复制代码
使用 EditorWindow 显示 TreeView:
  1. using UnityEditor;
  2. using UnityEditor.IMGUI.Controls;
  3. using UnityEngine;
  4. public class SimpleTreeViewWindow : EditorWindow
  5. {
  6.     // SerializeField is used to ensure the view state is written to the window
  7.     // layout file. This means that the state survives restarting Unity as long as the window
  8.     // is not closed. If the attribute is omitted then the state is still serialized/deserialized.
  9.     [SerializeField]
  10.     TreeViewState m_TreeViewState;
  11.     //The TreeView is not serializable, so it should be reconstructed from the tree data.
  12.     SimpleTreeView m_SimpleTreeView;
  13.     void OnEnable()
  14.     {
  15.         // Check whether there is already a serialized view state (state
  16.         // that survived assembly reloading)
  17.         if (m_TreeViewState == null)
  18.             m_TreeViewState = new TreeViewState();
  19.         
  20.         //创建 TreeView
  21.         m_SimpleTreeView = new SimpleTreeView(m_TreeViewState);
  22.     }
  23.     void OnGUI()
  24.     {
  25.         //绘制 TreeView
  26.         m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
  27.     }
  28.     // Add menu named "My Window" to the Window menu
  29.     [MenuItem("TreeView Examples/Simple Tree Window")]
  30.     static void ShowWindow()
  31.     {
  32.         // Get existing open window or if none, make a new one:
  33.         var window = GetWindow<SimpleTreeViewWindow>();
  34.         window.titleContent = new GUIContent("SimpleTreeView Window");
  35.         window.Show();
  36.     }
  37. }
复制代码
Example 2: A multi-column TreeView:

最终效果:


(列Header鼠标右键菜单)
EditorWindow:
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor.Callbacks;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine;
  6. namespace UnityEditor.TreeViewExamples
  7. {
  8.         class MultiColumnWindow : EditorWindow
  9.         {
  10.                 [NonSerialized] bool m_Initialized;
  11.         // Serialized in the window layout file so it survives assembly reloading
  12.         [SerializeField] TreeViewState m_TreeViewState;
  13.                 [SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
  14.                 SearchField m_SearchField;
  15.                 MultiColumnTreeView m_TreeView;
  16.                 MyTreeAsset m_MyTreeAsset;
  17.                 [MenuItem("TreeView Examples/Multi Columns")]
  18.                 public static MultiColumnWindow GetWindow ()
  19.                 {
  20.                         var window = GetWindow<MultiColumnWindow>();
  21.                         window.titleContent = new GUIContent("Multi Columns");
  22.                         window.Focus();
  23.                         window.Repaint();
  24.                         return window;
  25.                 }
  26.                 //双击资源时触发
  27.                 [OnOpenAsset]
  28.                 public static bool OnOpenAsset (int instanceID, int line)
  29.                 {
  30.                         var myTreeAsset = EditorUtility.InstanceIDToObject (instanceID) as MyTreeAsset;
  31.                         if (myTreeAsset != null)
  32.                         {
  33.                                 var window = GetWindow ();
  34.                                 window.SetTreeAsset(myTreeAsset);
  35.                                 return true;
  36.                         }
  37.                         return false; // we did not handle the open
  38.                 }
  39.                 void SetTreeAsset (MyTreeAsset myTreeAsset)
  40.                 {
  41.                         m_MyTreeAsset = myTreeAsset;
  42.                         m_Initialized = false;
  43.                 }
  44.                 // TreeView 显示区域
  45.                 Rect multiColumnTreeViewRect
  46.                 {
  47.                         get { return new Rect(20, 30, position.width-40, position.height-60); }
  48.                 }
  49.                 // 搜索显示区域
  50.                 Rect toolbarRect
  51.                 {
  52.                         get { return new Rect (20f, 10f, position.width-40f, 20f); }
  53.                 }
  54.                 //底部工具栏区域
  55.                 Rect bottomToolbarRect
  56.                 {
  57.                         get { return new Rect(20f, position.height - 18f, position.width - 40f, 16f); }
  58.                 }
  59.                 public MultiColumnTreeView treeView
  60.                 {
  61.                         get { return m_TreeView; }
  62.                 }
  63.                 void InitIfNeeded ()
  64.                 {
  65.                         if (!m_Initialized)
  66.                         {
  67.                                 // Check if it already exists (deserialized from window layout file or scriptable object)
  68.                                 if (m_TreeViewState == null)
  69.                                         m_TreeViewState = new TreeViewState();
  70.                 //创建 HeaderState,包含各列头部ui相关显示
  71.                 bool firstInit = m_MultiColumnHeaderState == null;
  72.                                 var headerState = MultiColumnTreeView.CreateDefaultMultiColumnHeaderState(multiColumnTreeViewRect.width);
  73.                                 //应用本地数据
  74.                                 if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState))
  75.                                         MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState);
  76.                                 m_MultiColumnHeaderState = headerState;
  77.                 //根据 HeaderState 创建 MultiColumnHeader
  78.                 var multiColumnHeader = new MyMultiColumnHeader(headerState);
  79.                                 if (firstInit)
  80.                                         multiColumnHeader.ResizeToFit ();
  81.                                 var treeModel = new TreeModel<MyTreeElement>(GetData());
  82.                 //根据 TreeViewState,MultiColumnHeader, TreeModel 创建 TreeView
  83.                 m_TreeView = new MultiColumnTreeView(m_TreeViewState, multiColumnHeader, treeModel);
  84.                                 //搜索输入框
  85.                                 m_SearchField = new SearchField();
  86.                                 m_SearchField.downOrUpArrowKeyPressed += m_TreeView.SetFocusAndEnsureSelectedItem;
  87.                                 m_Initialized = true;
  88.                         }
  89.                 }
  90.                
  91.                 // 获取列表数据
  92.                 IList<MyTreeElement> GetData ()
  93.                 {
  94.                         if (m_MyTreeAsset != null && m_MyTreeAsset.treeElements != null && m_MyTreeAsset.treeElements.Count > 0)
  95.                                 return m_MyTreeAsset.treeElements;
  96.                         // generate some test data
  97.                         return MyTreeElementGenerator.GenerateRandomTree(130);
  98.                 }
  99.                 // 选择某个资源时触发
  100.                 void OnSelectionChange ()
  101.                 {
  102.                         if (!m_Initialized)
  103.                                 return;
  104.                         var myTreeAsset = Selection.activeObject as MyTreeAsset;
  105.                         if (myTreeAsset != null && myTreeAsset != m_MyTreeAsset)
  106.                         {
  107.                                 m_MyTreeAsset = myTreeAsset;
  108.                                 m_TreeView.treeModel.SetData (GetData ());
  109.                                 m_TreeView.Reload ();
  110.                         }
  111.                 }
  112.                 void OnGUI ()
  113.                 {
  114.                         //初始化
  115.                         InitIfNeeded();
  116.                         SearchBar (toolbarRect);
  117.                         DoTreeView (multiColumnTreeViewRect);
  118.                         BottomToolBar (bottomToolbarRect);
  119.                 }
  120.                 void SearchBar (Rect rect)
  121.                 {
  122.                         //绘制搜索输入框
  123.                         treeView.searchString = m_SearchField.OnGUI (rect, treeView.searchString);
  124.                 }
  125.                 void DoTreeView (Rect rect)
  126.                 {
  127.                         //绘制 TreeView
  128.                         m_TreeView.OnGUI(rect);
  129.                 }
  130.                 //底部工具栏
  131.                 void BottomToolBar (Rect rect)
  132.                 {
  133.                         GUILayout.BeginArea (rect);
  134.                         using (new EditorGUILayout.HorizontalScope ())
  135.                         {
  136.                                 var style = "miniButton";
  137.                                 if (GUILayout.Button("Expand All", style))
  138.                                 {
  139.                                         treeView.ExpandAll ();
  140.                                 }
  141.                                 if (GUILayout.Button("Collapse All", style))
  142.                                 {
  143.                                         treeView.CollapseAll ();
  144.                                 }
  145.                                 GUILayout.FlexibleSpace();
  146.                                 GUILayout.Label (m_MyTreeAsset != null ? AssetDatabase.GetAssetPath (m_MyTreeAsset) : string.Empty);
  147.                                 GUILayout.FlexibleSpace ();
  148.                                 if (GUILayout.Button("Set sorting", style))
  149.                                 {
  150.                                         var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
  151.                                         myColumnHeader.SetSortingColumns (new int[] {4, 3, 2}, new[] {true, false, true});
  152.                                         myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
  153.                                 }
  154.                                 GUILayout.Label ("Header: ", "minilabel");
  155.                                 if (GUILayout.Button("Large", style))
  156.                                 {
  157.                                         var myColumnHeader = (MyMultiColumnHeader) treeView.multiColumnHeader;
  158.                                         myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
  159.                                 }
  160.                                 if (GUILayout.Button("Default", style))
  161.                                 {
  162.                                         var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
  163.                                         myColumnHeader.mode = MyMultiColumnHeader.Mode.DefaultHeader;
  164.                                 }
  165.                                 if (GUILayout.Button("No sort", style))
  166.                                 {
  167.                                         var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
  168.                                         myColumnHeader.mode = MyMultiColumnHeader.Mode.MinimumHeaderWithoutSorting;
  169.                                 }
  170.                                 GUILayout.Space (10);
  171.                                
  172.                                 if (GUILayout.Button("values <-> controls", style))
  173.                                 {
  174.                                         treeView.showControls = !treeView.showControls;
  175.                                 }
  176.                         }
  177.                         GUILayout.EndArea();
  178.                 }
  179.         }
  180.         internal class MyMultiColumnHeader : MultiColumnHeader
  181.         {
  182.                 Mode m_Mode;
  183.                 public enum Mode
  184.                 {
  185.                         LargeHeader,
  186.                         DefaultHeader,
  187.                         MinimumHeaderWithoutSorting
  188.                 }
  189.                 public MyMultiColumnHeader(MultiColumnHeaderState state)
  190.                         : base(state)
  191.                 {
  192.                         mode = Mode.DefaultHeader;
  193.                 }
  194.                 public Mode mode
  195.                 {
  196.                         get
  197.                         {
  198.                                 return m_Mode;
  199.                         }
  200.                         set
  201.                         {
  202.                                 m_Mode = value;
  203.                                 switch (m_Mode)
  204.                                 {
  205.                                         case Mode.LargeHeader:
  206.                                                 canSort = true;
  207.                                                 height = 37f;
  208.                                                 break;
  209.                                         case Mode.DefaultHeader:
  210.                                                 canSort = true;
  211.                                                 height = DefaultGUI.defaultHeight;
  212.                                                 break;
  213.                                         case Mode.MinimumHeaderWithoutSorting:
  214.                                                 canSort = false;
  215.                                                 height = DefaultGUI.minimumHeight;
  216.                                                 break;
  217.                                 }
  218.                         }
  219.                 }
  220.                 // 自定义 Head GUI 显示
  221.                 protected override void ColumnHeaderGUI (MultiColumnHeaderState.Column column, Rect headerRect, int columnIndex)
  222.                 {
  223.                         // Default column header gui
  224.                         base.ColumnHeaderGUI(column, headerRect, columnIndex);
  225.                         // Add additional info for large header
  226.                         if (mode == Mode.LargeHeader)
  227.                         {
  228.                                 // Show example overlay stuff on some of the columns
  229.                                 if (columnIndex > 2)
  230.                                 {
  231.                                         headerRect.xMax -= 3f;
  232.                                         var oldAlignment = EditorStyles.largeLabel.alignment;
  233.                                         EditorStyles.largeLabel.alignment = TextAnchor.UpperRight;
  234.                                         GUI.Label(headerRect, 36 + columnIndex + "%", EditorStyles.largeLabel);
  235.                                         EditorStyles.largeLabel.alignment = oldAlignment;
  236.                                 }
  237.                         }
  238.                 }
  239.         }
  240. }
复制代码
MultiColumnTreeView:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine;
  6. using UnityEngine.Assertions;
  7. namespace UnityEditor.TreeViewExamples
  8. {
  9.         internal class MultiColumnTreeView : TreeViewWithTreeModel<MyTreeElement>
  10.         {
  11.                 const float kRowHeights = 20f;
  12.                 const float kToggleWidth = 18f;
  13.                 public bool showControls = true;
  14.                 //编辑器图标资源
  15.                 static GUIContent[] s_TestIcons =
  16.                 {
  17.             EditorGUIUtility.IconContent ("Folder Icon"),
  18.                         EditorGUIUtility.IconContent ("AudioSource Icon"),
  19.                         EditorGUIUtility.IconContent ("Camera Icon"),
  20.                         EditorGUIUtility.IconContent ("Windzone Icon"),
  21.                         EditorGUIUtility.IconContent ("GameObject Icon")
  22.                 };
  23.                 // All columns
  24.                 enum MyColumns
  25.                 {
  26.                         Icon1,
  27.                         Icon2,
  28.                         Name,
  29.                         Value1,
  30.                         Value2,
  31.                         Value3,
  32.                 }
  33.                 public enum SortOption
  34.                 {
  35.                         Name,
  36.                         Value1,
  37.                         Value2,
  38.                         Value3,
  39.                 }
  40.                 // Sort options per column
  41.                 SortOption[] m_SortOptions =
  42.                 {
  43.                         SortOption.Value1,
  44.                         SortOption.Value3,
  45.                         SortOption.Name,
  46.                         SortOption.Value1,
  47.                         SortOption.Value2,
  48.                         SortOption.Value3
  49.                 };
  50.                 //展开Tree,扁平处理,不包含Root
  51.                 public static void TreeToList (TreeViewItem root, IList<TreeViewItem> result)
  52.                 {
  53.                         if (root == null)
  54.                                 throw new NullReferenceException("root");
  55.                         if (result == null)
  56.                                 throw new NullReferenceException("result");
  57.                         result.Clear();
  58.        
  59.                         if (root.children == null)
  60.                                 return;
  61.                         //通过一个堆来展开
  62.                         Stack<TreeViewItem> stack = new Stack<TreeViewItem>();
  63.                         //后面的元素先入栈,后弹出
  64.                         for (int i = root.children.Count - 1; i >= 0; i--)
  65.                                 stack.Push(root.children[i]);
  66.                         while (stack.Count > 0)
  67.                         {
  68.                                 TreeViewItem current = stack.Pop();
  69.                                 result.Add(current);
  70.                                 if (current.hasChildren && current.children[0] != null)
  71.                                 {
  72.                                         for (int i = current.children.Count - 1; i >= 0; i--)
  73.                                         {
  74.                                                 stack.Push(current.children[i]);
  75.                                         }
  76.                                 }
  77.                         }
  78.                 }
  79.                 public MultiColumnTreeView (TreeViewState state, MultiColumnHeader multicolumnHeader, TreeModel<MyTreeElement> model) : base (state, multicolumnHeader, model)
  80.                 {
  81.                         Assert.AreEqual(m_SortOptions.Length , Enum.GetValues(typeof(MyColumns)).Length, "Ensure number of sort options are in sync with number of MyColumns enum values");
  82.                         // Custom setup
  83.                         rowHeight = kRowHeights;                                //行高
  84.                         columnIndexForTreeFoldouts = 2;                        //折叠图标在哪列显示
  85.                         showAlternatingRowBackgrounds = true;        //背景交替变化
  86.                         showBorder = true;                                                //显示边框
  87.                         customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f; // center foldout in the row since we also center content. See RowGUI
  88.                         extraSpaceBeforeIconAndLabel = kToggleWidth;
  89.                         multicolumnHeader.sortingChanged += OnSortingChanged;        //排序发生变化
  90.                        
  91.                         Reload();
  92.                 }
  93.         //构建显示的行 TreeViewItem 数据
  94.         // Note we We only build the visible rows, only the backend has the full tree information.
  95.         // The treeview only creates info for the row list.
  96.         protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
  97.                 {
  98.                         var rows = base.BuildRows (root);
  99.                         SortIfNeeded (root, rows);
  100.                         return rows;
  101.                 }
  102.                 void OnSortingChanged (MultiColumnHeader multiColumnHeader)
  103.                 {
  104.                         SortIfNeeded (rootItem, GetRows());
  105.                 }
  106.                 void SortIfNeeded (TreeViewItem root, IList<TreeViewItem> rows)
  107.                 {
  108.                         if (rows.Count <= 1)
  109.                                 return;
  110.                        
  111.                         if (multiColumnHeader.sortedColumnIndex == -1)
  112.                         {
  113.                                 return; // No column to sort for (just use the order the data are in)
  114.                         }
  115.                        
  116.                         // Sort the roots of the existing tree items
  117.                         SortByMultipleColumns ();
  118.                         TreeToList(root, rows);
  119.                         Repaint();
  120.                 }
  121.                 void SortByMultipleColumns ()
  122.                 {
  123.                         //设置排序的列
  124.                         var sortedColumns = multiColumnHeader.state.sortedColumns;
  125.                         if (sortedColumns.Length == 0)
  126.                                 return;
  127.                         //只排序根节点直属的子节点
  128.                         var myTypes = rootItem.children.Cast<TreeViewItem<MyTreeElement> >();
  129.                         //首选列优先排序
  130.                         var orderedQuery = InitialOrder (myTypes, sortedColumns);
  131.                         //其他列排序
  132.                         for (int i=1; i<sortedColumns.Length; i++)
  133.                         {
  134.                                 SortOption sortOption = m_SortOptions[sortedColumns[i]];
  135.                                 bool ascending = multiColumnHeader.IsSortedAscending(sortedColumns[i]);
  136.                                 switch (sortOption)
  137.                                 {
  138.                                         case SortOption.Name:
  139.                         // ThenBy 在现有排序的基础上,对于相等的元素,再深入排序
  140.                         orderedQuery = orderedQuery.ThenBy(l => l.data.name, ascending);
  141.                                                 break;
  142.                                         case SortOption.Value1:
  143.                                                 orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue1, ascending);
  144.                                                 break;
  145.                                         case SortOption.Value2:
  146.                                                 orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue2, ascending);
  147.                                                 break;
  148.                                         case SortOption.Value3:
  149.                                                 orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue3, ascending);
  150.                                                 break;
  151.                                 }
  152.                         }
  153.                         rootItem.children = orderedQuery.Cast<TreeViewItem> ().ToList ();
  154.                 }
  155.                 IOrderedEnumerable<TreeViewItem<MyTreeElement>> InitialOrder(IEnumerable<TreeViewItem<MyTreeElement>> myTypes, int[] history)
  156.                 {
  157.                         SortOption sortOption = m_SortOptions[history[0]];
  158.                         //是否升序
  159.                         bool ascending = multiColumnHeader.IsSortedAscending(history[0]);
  160.                         switch (sortOption)
  161.                         {
  162.                                 case SortOption.Name:
  163.                                         return myTypes.Order(l => l.data.name, ascending);
  164.                                 case SortOption.Value1:
  165.                                         return myTypes.Order(l => l.data.floatValue1, ascending);
  166.                                 case SortOption.Value2:
  167.                                         return myTypes.Order(l => l.data.floatValue2, ascending);
  168.                                 case SortOption.Value3:
  169.                                         return myTypes.Order(l => l.data.floatValue3, ascending);
  170.                                 default:
  171.                                         Assert.IsTrue(false, "Unhandled enum");
  172.                                         break;
  173.                         }
  174.                         // default
  175.                         return myTypes.Order(l => l.data.name, ascending);
  176.                 }
  177.                 int GetIcon1Index(TreeViewItem<MyTreeElement> item)
  178.                 {
  179.                         return (int)(Mathf.Min(0.99f, item.data.floatValue1) * s_TestIcons.Length);
  180.                 }
  181.                 int GetIcon2Index (TreeViewItem<MyTreeElement> item)
  182.                 {
  183.                         return Mathf.Min(item.data.text.Length, s_TestIcons.Length-1);
  184.                 }
  185.                 //绘制一行的 GUI
  186.                 protected override void RowGUI (RowGUIArgs args)
  187.                 {
  188.                         var item = (TreeViewItem<MyTreeElement>) args.item;
  189.             //列可通过Header菜单隐藏,故需要 args.GetNumVisibleColumns
  190.             for (int i = 0; i < args.GetNumVisibleColumns (); ++i)
  191.                         {
  192.                                 //绘制每列
  193.                                 CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
  194.                         }
  195.                 }
  196.                 void CellGUI (Rect cellRect, TreeViewItem<MyTreeElement> item, MyColumns column, ref RowGUIArgs args)
  197.                 {
  198.                         // Center cell rect vertically (makes it easier to place controls, icons etc in the cells)
  199.                         CenterRectUsingSingleLineHeight(ref cellRect);
  200.                         //根据类型绘制 GUI
  201.                         switch (column)
  202.                         {
  203.                                 case MyColumns.Icon1:
  204.                                         {
  205.                                                 GUI.DrawTexture(cellRect, s_TestIcons[GetIcon1Index(item)].image, ScaleMode.ScaleToFit);
  206.                                         }
  207.                                         break;
  208.                                 case MyColumns.Icon2:
  209.                                         {
  210.                                                 GUI.DrawTexture(cellRect, s_TestIcons[GetIcon2Index(item)].image, ScaleMode.ScaleToFit);
  211.                                         }
  212.                                         break;
  213.                                 case MyColumns.Name:
  214.                                         {
  215.                                                 // Do toggle
  216.                                                 Rect toggleRect = cellRect;
  217.                                                 toggleRect.x += GetContentIndent(item);
  218.                                                 toggleRect.width = kToggleWidth;
  219.                                                 if (toggleRect.xMax < cellRect.xMax)
  220.                                                         item.data.enabled = EditorGUI.Toggle(toggleRect, item.data.enabled); // hide when outside cell rect
  221.                                                 // Default icon and label
  222.                                                 args.rowRect = cellRect;
  223.                                                 base.RowGUI(args);
  224.                                         }
  225.                                         break;
  226.                                 case MyColumns.Value1:
  227.                                 case MyColumns.Value2:
  228.                                 case MyColumns.Value3:
  229.                                         {
  230.                                                 if (showControls)
  231.                                                 {
  232.                                                         cellRect.xMin += 5f; // When showing controls make some extra spacing
  233.                                                         if (column == MyColumns.Value1)
  234.                                                                 item.data.floatValue1 = EditorGUI.Slider(cellRect, GUIContent.none, item.data.floatValue1, 0f, 1f);
  235.                                                         if (column == MyColumns.Value2)
  236.                                                                 item.data.material = (Material)EditorGUI.ObjectField(cellRect, GUIContent.none, item.data.material, typeof(Material), false);
  237.                                                         if (column == MyColumns.Value3)
  238.                                                                 item.data.text = GUI.TextField(cellRect, item.data.text);
  239.                                                 }
  240.                                                 else
  241.                                                 {
  242.                                                         string value = "Missing";
  243.                                                         if (column == MyColumns.Value1)
  244.                                                                 value = item.data.floatValue1.ToString("f5");
  245.                                                         if (column == MyColumns.Value2)
  246.                                                                 value = item.data.floatValue2.ToString("f5");
  247.                                                         if (column == MyColumns.Value3)
  248.                                                                 value = item.data.floatValue3.ToString("f5");
  249.                                                         DefaultGUI.LabelRightAligned(cellRect, value, args.selected, args.focused);
  250.                                                 }
  251.                                         }
  252.                                         break;
  253.                         }
  254.                 }
  255.                 // Rename
  256.                 //--------
  257.                 protected override bool CanRename(TreeViewItem item)
  258.                 {
  259.                         // Only allow rename if we can show the rename overlay with a certain width (label might be clipped by other columns)
  260.                         Rect renameRect = GetRenameRect (treeViewRect, 0, item);
  261.                         return renameRect.width > 30;
  262.                 }
  263.                 protected override void RenameEnded(RenameEndedArgs args)
  264.                 {
  265.                         // Set the backend name and reload the tree to reflect the new model
  266.                         if (args.acceptedRename)
  267.                         {
  268.                                 var element = treeModel.Find(args.itemID);
  269.                                 element.name = args.newName;
  270.                                 Reload();
  271.                         }
  272.                 }
  273.                 protected override Rect GetRenameRect (Rect rowRect, int row, TreeViewItem item)
  274.                 {
  275.                         Rect cellRect = GetCellRectForTreeFoldouts (rowRect);
  276.                         CenterRectUsingSingleLineHeight(ref cellRect);
  277.                         return base.GetRenameRect (cellRect, row, item);
  278.                 }
  279.                 // Misc
  280.                 //--------
  281.                 protected override bool CanMultiSelect (TreeViewItem item)
  282.                 {
  283.                         return true;
  284.                 }
  285.         //创建 HeaderState
  286.         public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth)
  287.                 {
  288.                         var columns = new[]
  289.                         {
  290.                                 new MultiColumnHeaderState.Column
  291.                                 {
  292.                                         headerContent = new GUIContent(EditorGUIUtility.FindTexture("FilterByLabel"), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
  293.                                         contextMenuText = "Asset",                //右键点击出现的菜单,可配合 allowToggleVisibility 来控制该列是否显示
  294.                                         headerTextAlignment = TextAlignment.Center,
  295.                                         sortedAscending = true,
  296.                                         sortingArrowAlignment = TextAlignment.Right,
  297.                                         width = 30,
  298.                                         minWidth = 30,
  299.                                         maxWidth = 60,
  300.                                         autoResize = false,
  301.                                         allowToggleVisibility = true
  302.                                 },
  303.                                 new MultiColumnHeaderState.Column
  304.                                 {
  305.                                         headerContent = new GUIContent(EditorGUIUtility.FindTexture("FilterByType"), "Sed hendrerit mi enim, eu iaculis leo tincidunt at."),
  306.                                         contextMenuText = "Type",
  307.                                         headerTextAlignment = TextAlignment.Center,
  308.                                         sortedAscending = true,
  309.                                         sortingArrowAlignment = TextAlignment.Right,
  310.                                         width = 30,
  311.                                         minWidth = 30,
  312.                                         maxWidth = 60,
  313.                                         autoResize = false,
  314.                                         allowToggleVisibility = true
  315.                                 },
  316.                                 new MultiColumnHeaderState.Column
  317.                                 {
  318.                                         headerContent = new GUIContent("Name"),
  319.                                         headerTextAlignment = TextAlignment.Left,
  320.                                         sortedAscending = true,
  321.                                         sortingArrowAlignment = TextAlignment.Center,
  322.                                         width = 150,
  323.                                         minWidth = 60,
  324.                                         autoResize = false,
  325.                                         allowToggleVisibility = false
  326.                                 },
  327.                                 new MultiColumnHeaderState.Column
  328.                                 {
  329.                                         headerContent = new GUIContent("Multiplier", "In sed porta ante. Nunc et nulla mi."),
  330.                                         headerTextAlignment = TextAlignment.Right,
  331.                                         sortedAscending = true,
  332.                                         sortingArrowAlignment = TextAlignment.Left,
  333.                                         width = 110,
  334.                                         minWidth = 60,
  335.                                         autoResize = true
  336.                                 },
  337.                                 new MultiColumnHeaderState.Column
  338.                                 {
  339.                                         headerContent = new GUIContent("Material", "Maecenas congue non tortor eget vulputate."),
  340.                                         headerTextAlignment = TextAlignment.Right,
  341.                                         sortedAscending = true,
  342.                                         sortingArrowAlignment = TextAlignment.Left,
  343.                                         width = 95,
  344.                                         minWidth = 60,
  345.                                         autoResize = true,
  346.                                         allowToggleVisibility = true
  347.                                 },
  348.                                 new MultiColumnHeaderState.Column
  349.                                 {
  350.                                         headerContent = new GUIContent("Note", "Nam at tellus ultricies ligula vehicula ornare sit amet quis metus."),
  351.                                         headerTextAlignment = TextAlignment.Right,
  352.                                         sortedAscending = true,
  353.                                         sortingArrowAlignment = TextAlignment.Left,
  354.                                         width = 70,
  355.                                         minWidth = 60,
  356.                                         autoResize = true
  357.                                 }
  358.                         };
  359.                         Assert.AreEqual(columns.Length, Enum.GetValues(typeof(MyColumns)).Length, "Number of columns should match number of enum values: You probably forgot to update one of them.");
  360.                         var state =  new MultiColumnHeaderState(columns);
  361.                         return state;
  362.                 }
  363.         }
  364.         static class MyExtensionMethods
  365.         {
  366.                 public static IOrderedEnumerable<T> Order<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector, bool ascending)
  367.                 {
  368.                         if (ascending)
  369.                         {
  370.                                 return source.OrderBy(selector);
  371.                         }
  372.                         else
  373.                         {
  374.                                 return source.OrderByDescending(selector);
  375.                         }
  376.                 }
  377.                 public static IOrderedEnumerable<T> ThenBy<T, TKey>(this IOrderedEnumerable<T> source, Func<T, TKey> selector, bool ascending)
  378.                 {
  379.                         if (ascending)
  380.                         {
  381.                                 return source.ThenBy(selector);
  382.                         }
  383.                         else
  384.                         {
  385.                                 return source.ThenByDescending(selector);
  386.                         }
  387.                 }
  388.         }
  389. }
复制代码
基类 TreeViewWithTreeModel:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine;
  6. namespace UnityEditor.TreeViewExamples
  7. {
  8.         internal class TreeViewItem<T> : TreeViewItem where T : TreeElement
  9.         {
  10.                 public T data { get; set; }
  11.                 public TreeViewItem (int id, int depth, string displayName, T data) : base (id, depth, displayName)
  12.                 {
  13.                         this.data = data;
  14.                 }
  15.         }
  16.         internal class TreeViewWithTreeModel<T> : TreeView where T : TreeElement
  17.         {
  18.                 TreeModel<T> m_TreeModel;
  19.                 readonly List<TreeViewItem> m_Rows = new List<TreeViewItem>(100);
  20.                 public event Action treeChanged;
  21.                 public TreeModel<T> treeModel { get { return m_TreeModel; } }
  22.                 public event Action<IList<TreeViewItem>>  beforeDroppingDraggedItems;
  23.                 public TreeViewWithTreeModel (TreeViewState state, TreeModel<T> model) : base (state)
  24.                 {
  25.                         Init (model);
  26.                 }
  27.                 public TreeViewWithTreeModel (TreeViewState state, MultiColumnHeader multiColumnHeader, TreeModel<T> model)
  28.                         : base(state, multiColumnHeader)
  29.                 {
  30.                         Init (model);
  31.                 }
  32.                 void Init (TreeModel<T> model)
  33.                 {
  34.                         m_TreeModel = model;
  35.                         m_TreeModel.modelChanged += ModelChanged;
  36.                 }
  37.                 void ModelChanged ()
  38.                 {
  39.                         if (treeChanged != null)
  40.                                 treeChanged ();
  41.                         Reload ();
  42.                 }
  43.                 //构建根节点
  44.                 protected override TreeViewItem BuildRoot()
  45.                 {
  46.                         int depthForHiddenRoot = -1;
  47.                         return new TreeViewItem<T>(m_TreeModel.root.id, depthForHiddenRoot, m_TreeModel.root.name, m_TreeModel.root);
  48.                 }
  49.         //构建显示的行 TreeViewItem
  50.         protected override IList<TreeViewItem> BuildRows (TreeViewItem root)
  51.                 {
  52.                         if (m_TreeModel.root == null)
  53.                         {
  54.                                 Debug.LogError ("tree model root is null. did you call SetData()?");
  55.                         }
  56.                         m_Rows.Clear ();
  57.                         if (!string.IsNullOrEmpty(searchString))
  58.                         {
  59.                                 Search (m_TreeModel.root, searchString, m_Rows);
  60.                         }
  61.                         else
  62.                         {
  63.                                 if (m_TreeModel.root.hasChildren)
  64.                                         AddChildrenRecursive(m_TreeModel.root, 0, m_Rows);
  65.                         }
  66.                         // We still need to setup the child parent information for the rows since this
  67.                         // information is used by the TreeView internal logic (navigation, dragging etc)
  68.                         SetupParentsAndChildrenFromDepths (root, m_Rows);
  69.                         return m_Rows;
  70.                 }
  71.                 void AddChildrenRecursive (T parent, int depth, IList<TreeViewItem> newRows)
  72.                 {
  73.                         foreach (T child in parent.children)
  74.                         {
  75.                                 var item = new TreeViewItem<T>(child.id, depth, child.name, child);
  76.                                 newRows.Add(item);
  77.                                 if (child.hasChildren)
  78.                                 {
  79.                                         if (IsExpanded(child.id))        //展开显示的行
  80.                                         {
  81.                                                 AddChildrenRecursive (child, depth + 1, newRows);
  82.                                         }
  83.                                         else
  84.                                         {
  85.                                                 item.children = CreateChildListForCollapsedParent();
  86.                                         }
  87.                                 }
  88.                         }
  89.                 }
  90.                 void Search(T searchFromThis, string search, List<TreeViewItem> result)
  91.                 {
  92.                         if (string.IsNullOrEmpty(search))
  93.                                 throw new ArgumentException("Invalid search: cannot be null or empty", "search");
  94.                         const int kItemDepth = 0; // tree is flattened when searching
  95.                         Stack<T> stack = new Stack<T>();
  96.                         foreach (var element in searchFromThis.children)
  97.                                 stack.Push((T)element);
  98.                         while (stack.Count > 0)
  99.                         {
  100.                                 T current = stack.Pop();
  101.                                 // 名字匹配的行
  102.                                 if (current.name.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
  103.                                 {
  104.                                         result.Add(new TreeViewItem<T>(current.id, kItemDepth, current.name, current));
  105.                                 }
  106.                                 if (current.children != null && current.children.Count > 0)
  107.                                 {
  108.                                         foreach (var element in current.children)
  109.                                         {
  110.                                                 stack.Push((T)element);
  111.                                         }
  112.                                 }
  113.                         }
  114.                         SortSearchResult(result);
  115.                 }
  116.                 protected virtual void SortSearchResult (List<TreeViewItem> rows)
  117.                 {
  118.                         rows.Sort ((x,y) => EditorUtility.NaturalCompare (x.displayName, y.displayName)); // sort by displayName by default, can be overriden for multicolumn solutions
  119.                 }
  120.        
  121.                 protected override IList<int> GetAncestors (int id)
  122.                 {
  123.                         return m_TreeModel.GetAncestors(id);
  124.                 }
  125.                 protected override IList<int> GetDescendantsThatHaveChildren (int id)
  126.                 {
  127.                         return m_TreeModel.GetDescendantsThatHaveChildren(id);
  128.                 }
  129.                 // Dragging
  130.                 //-----------
  131.        
  132.                 const string k_GenericDragID = "GenericDragColumnDragging";
  133.                 protected override bool CanStartDrag (CanStartDragArgs args)
  134.                 {
  135.                         return true;
  136.                 }
  137.                 protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
  138.                 {
  139.                         if (hasSearch)
  140.                                 return;
  141.                         DragAndDrop.PrepareStartDrag();
  142.                         var draggedRows = GetRows().Where(item => args.draggedItemIDs.Contains(item.id)).ToList();
  143.                         DragAndDrop.SetGenericData(k_GenericDragID, draggedRows);
  144.                         DragAndDrop.objectReferences = new UnityEngine.Object[] { }; // this IS required for dragging to work
  145.                         string title = draggedRows.Count == 1 ? draggedRows[0].displayName : "< Multiple >";
  146.                         DragAndDrop.StartDrag (title);
  147.                 }
  148.                 protected override DragAndDropVisualMode HandleDragAndDrop (DragAndDropArgs args)
  149.                 {
  150.                         // Check if we can handle the current drag data (could be dragged in from other areas/windows in the editor)
  151.                         var draggedRows = DragAndDrop.GetGenericData(k_GenericDragID) as List<TreeViewItem>;
  152.                         if (draggedRows == null)
  153.                                 return DragAndDropVisualMode.None;
  154.                         // Parent item is null when dragging outside any tree view items.
  155.                         switch (args.dragAndDropPosition)
  156.                         {
  157.                                 case DragAndDropPosition.UponItem:
  158.                                 case DragAndDropPosition.BetweenItems:
  159.                                         {
  160.                                                 bool validDrag = ValidDrag(args.parentItem, draggedRows);
  161.                                                 if (args.performDrop && validDrag)
  162.                                                 {
  163.                                                         T parentData = ((TreeViewItem<T>)args.parentItem).data;
  164.                                                         OnDropDraggedElementsAtIndex(draggedRows, parentData, args.insertAtIndex == -1 ? 0 : args.insertAtIndex);
  165.                                                 }
  166.                                                 return validDrag ? DragAndDropVisualMode.Move : DragAndDropVisualMode.None;
  167.                                         }
  168.                                 case DragAndDropPosition.OutsideItems:
  169.                                         {
  170.                                                 if (args.performDrop)
  171.                                                         OnDropDraggedElementsAtIndex(draggedRows, m_TreeModel.root, m_TreeModel.root.children.Count);
  172.                                                 return DragAndDropVisualMode.Move;
  173.                                         }
  174.                                 default:
  175.                                         Debug.LogError("Unhandled enum " + args.dragAndDropPosition);
  176.                                         return DragAndDropVisualMode.None;
  177.                         }
  178.                 }
  179.                 public virtual void OnDropDraggedElementsAtIndex (List<TreeViewItem> draggedRows, T parent, int insertIndex)
  180.                 {
  181.                         if (beforeDroppingDraggedItems != null)
  182.                                 beforeDroppingDraggedItems (draggedRows);
  183.                         var draggedElements = new List<TreeElement> ();
  184.                         foreach (var x in draggedRows)
  185.                                 draggedElements.Add (((TreeViewItem<T>) x).data);
  186.                
  187.                         var selectedIDs = draggedElements.Select (x => x.id).ToArray();
  188.                         m_TreeModel.MoveElements (parent, insertIndex, draggedElements);
  189.                         SetSelection(selectedIDs, TreeViewSelectionOptions.RevealAndFrame);
  190.                 }
  191.                 bool ValidDrag(TreeViewItem parent, List<TreeViewItem> draggedItems)
  192.                 {
  193.                         TreeViewItem currentParent = parent;
  194.                         while (currentParent != null)
  195.                         {
  196.                                 if (draggedItems.Contains(currentParent))
  197.                                         return false;
  198.                                 currentParent = currentParent.parent;
  199.                         }
  200.                         return true;
  201.                 }
  202.        
  203.         }
  204. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

吴旭华

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表