TreeView Examples project: 下载 TreeView Manual: 下载 TreeView API Documentation: 下载 MultiColumnHeader API Documentation: 下载
最近看项目中的编辑器模块,发现大佬们写的Unity编辑器工具非常优美,查阅代码发现并没有用很牛逼的插件,只是用原生的GUI和UnityEditor自带控件制作而成。于是,向大佬学习,学习一下UnityEditor的控件。 UnityEditor.IMGUI.Controls相关控件API如下(官网可查阅):
相关API:
TreeView:TreeView是一个IMGUI控件,可以让您为编辑器创建树视图、列表视图和多列表。它可以自定义行内容呈现、拖拽逻辑、选择逻辑、搜索、排序和项目重命名
自定义树形类继承自TreeView,必须显示调用TreeView中的有参构造,TreeView中不含有无参构造方法,无法隐式调用
BuildRoot():是一个需要被实现的抽象方法。该方法应该创建treeviewitem的完整树并返回根目录。这个方法和BuildRows一起实现TreeView的初始化工作。可以使用两种不同的方法创建一个TreeView:(1)创建项目的根和完整树;(2)创建根和行.方法1是默认的,因为TreeView会自动处理构建行,祖先信息等等;对于非常大的数据集或经常更改的数据,方法2是可取的.每个TreeViewItem都需要用唯一的整数ID构造,对于相同的数据元素,ID需要保持一致,不管是否是拓展状态。ID用于在树中查找项目,用于选择状态、扩展状态和导航。对于一个被合理初始化的TreeView,所有的treeviewitem都需要初始化“父”、“子”和“深度”属性。根据树的数据模型(Tree Model)可以有用的设置父和子的属性或深度属性,然后使用SetupParentsAndChildrenFromDepths或SetupDepthsFromParentsAndChildren方法在一次调用中设置所有行未初始化的属性。
BuildRows():重写该方法可以控制如何生成行。每当调用重载时或者每次扩展或收缩时,就会调用这个方法。构建行的默认实现负责根据完整的树和项目的扩展状态来缓存扩展的行。对于非常大的数据集或经常更改的数据,只需要创建TreeView的行,而不是完整的树。在这种情况下,重写该方法以手动构建行,如果遇到收缩的父节点,那么父节点的后代就可以被忽略,设置项的孩子用CreateChildListforCollapsedParent()方法。当使用这种方法时,构建root应该只创建根TreeViewItem,也确保重写GetAncestors()和GetDescendantsThatHaveChildren()和使用模型数据获取这些信息,否则构建框架和扩大子树将会失败。
SetupDepthsFromParentsAndChildren():使用输入TreeViewItem的深度为其所有后代TreeViewItem设置正确的深度。
SetupParentsAndChildrenFromDepths():使用已设置的顺序和深度值来初始化所有行的父和子属性.
TreeViewItem: 树形View中单个ItemTreeViewState: TreeViewState为TreeView提供可序列化的状态信息。这主要是用户可以通过与TreeView进行交互来改变的状态,例如选择状态,扩展状态,导航状态和滚动状态.TreeViewState是唯一应该在TreeView中序列化/反序列化的状态.TreeView本身不是可序列化的,应该从它所代表的树数据中进行重构.这个类中包含的所有状态都是由TreeView自身更新的。对这个状态的访问也可以通过TreeView API完成。SearchField: 搜索控件,对TreeView中TreeViewItem的displayName进行搜索,搜索后TreeView显示搜索结果 使用方式:searchField.downOrUpArrowKeyPressed += treeView.SetFocusAndEnsureSelectedItem; treeView.searchString = searchField.OnToolbarGUI(treeView.searchString);
树形控件效果图:
TreeViewWindow(窗口类) :
using UnityEngine; using UnityEditor; using UnityEditor.IMGUI.Controls; public class TreeViewWindow : EditorWindow { TreeViewState viewState; TestTreeView view; SearchField searchField; [MenuItem("TreeView学习/Text ccc")] static void OpenWindow() { TreeViewWindow window = GetWindow<TreeViewWindow>(); window.Show(); } private void OnEnable() { if (viewState == null) viewState = new TreeViewState(); view = new TestTreeView(viewState); searchField = new SearchField(); searchField.downOrUpArrowKeyPressed += view.SetFocusAndEnsureSelectedItem; } private void OnGUI() { DoToolbar(); DoTreeView(); DoBottomToolbar(); } /// <summary> /// 头部搜索框 /// </summary> private void DoToolbar() { GUILayout.BeginHorizontal(EditorStyles.toolbar); view.searchString = searchField.OnToolbarGUI(view.searchString); GUILayout.EndHorizontal(); } /// <summary> /// 内容树 /// </summary> private void DoTreeView() { Rect rect = new Rect(0, 20, position.width, position.height - 40); this.view.OnGUI(rect); } /// <summary> /// 底部toolbar /// </summary> private void DoBottomToolbar() { GUILayout.BeginArea(new Rect(0, position.height - 20, position.width, 20)); GUILayout.BeginHorizontal(); if (GUILayout.Button("展开所有")) { view.ExpandAll(); } if (GUILayout.Button("收缩所有")) { view.CollapseAll(); } GUILayout.EndHorizontal(); GUILayout.EndArea(); } }TestTreeView (树形View):
using System.Collections.Generic; using UnityEditor.IMGUI.Controls; //重写构造方法 //TreeView中不含有无参构造方法,无法隐式调用,必须显示调用TreeView中的一个有参构造 public class TestTreeView : TreeView { public TestTreeView(TreeViewState state) : base(state) { Reload(); } protected override TreeViewItem BuildRoot() { TreeViewItem root = new TreeViewItem(-1, -1, "root"); var allItems = new List<TreeViewItem> { new TreeViewItem {id = 1, depth = 0, displayName = "Animals"}, new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"}, new TreeViewItem {id = 3, depth = 2, displayName = "Tiger"}, new TreeViewItem {id = 4, depth = 2, displayName = "Elephant"}, new TreeViewItem {id = 5, depth = 2, displayName = "Okapi"}, new TreeViewItem {id = 6, depth = 2, displayName = "Armadillo"}, new TreeViewItem {id = 7, depth = 1, displayName = "Reptiles"}, new TreeViewItem {id = 8, depth = 2, displayName = "Crocodile"}, new TreeViewItem {id = 9, depth = 2, displayName = "Lizard"}, new TreeViewItem {id = 11, depth = 0, displayName = "Animals"}, new TreeViewItem {id = 12, depth = 1, displayName = "Mammals"}, new TreeViewItem {id = 13, depth = 2, displayName = "Tiger"}, new TreeViewItem {id = 14, depth = 2, displayName = "Elephant"}, new TreeViewItem {id = 15, depth = 2, displayName = "Okapi"}, new TreeViewItem {id = 16, depth = 2, displayName = "Armadillo"}, new TreeViewItem {id = 17, depth = 1, displayName = "Reptiles"}, new TreeViewItem {id = 18, depth = 2, displayName = "Crocodile"}, new TreeViewItem {id = 19, depth = 2, displayName = "Lizard"}, new TreeViewItem {id = 21, depth = 0, displayName = "Animals"}, new TreeViewItem {id = 22, depth = 1, displayName = "Mammals"}, new TreeViewItem {id = 23, depth = 2, displayName = "Tiger"}, new TreeViewItem {id = 24, depth = 2, displayName = "Elephant"}, new TreeViewItem {id = 25, depth = 2, displayName = "Okapi"}, new TreeViewItem {id = 26, depth = 2, displayName = "Armadillo"}, new TreeViewItem {id = 27, depth = 1, displayName = "Reptiles"}, new TreeViewItem {id = 28, depth = 2, displayName = "Crocodile"}, new TreeViewItem {id = 29, depth = 2, displayName = "Lizard"}, }; SetupParentsAndChildrenFromDepths(root, allItems); return root; } }