UI 工具包中最基本的构建块是视觉元素。视觉元素被组织为具有父子关系的层级结构树。下图显示了层级结构树的简化示例,以及 UI 工具包中的渲染结果。
VisualElement 类是视觉树中所有节点的公共基类。VisualElement
基类包含所有控件共有的属性,如样式、布局数据、事件处理程序等。视觉元素可以有子级和后代视觉元素。例如,在上图中,Box
视觉元素具有三个子视觉元素:Label
、Checkbox
和 Slider
。
您可以通过样式表自定义视觉元素的外观。还可以使用事件回调修改视觉元素的行为。
VisualElement
派生出若干定义了额外行为和功能的子类,如控件。UI 工具包提供了各种具有特定行为的内置控件。例如,以下项目是可用的内置控件:
您还可以将视觉元素组合在一起并修改其行为以创建自定义控件。有关内置控件的列表,请参阅控件参考页面。
面板是视觉树的父级对象。视觉树必须连接到面板才能渲染其中的视觉元素。所有面板都属于一个窗口,例如一个编辑器窗口。该面板还处理焦点控制和视觉树的事件调度。
视觉树中的每个元素都包含对包含视觉树的面板的直接引用。为了验证 VisualElement
是否已连接到面板,可以测试此元素的 panel
属性。当视觉元素未连接时,测试将返回 null
。
视觉树中元素的绘制顺序遵循深度优先搜索。子视觉元素显示在父元素之上。子元素也按同级列表的顺序绘制。绘制顺序如下:
下图显示了上一示例的绘制顺序:
要更改视觉元素绘制顺序,请使用以下函数:
对于同级视觉元素,请使用以下函数:
UI 工具包使用强大的布局系统,根据其样式属性中的布局参数自动计算各个元素的位置和大小。有关更多信息,请参阅布局引擎页面。
UI 工具包有两种坐标:
每个视觉元素确定用于计算其位置的坐标系。您可以在元素样式表中配置要使用的坐标系。
下面的代码演示如何通过代码设置一个视觉元素的坐标空间和位置:
var newElement = new VisualElement();
newElement.style.position = Position.Relative;
newElement.style.left = 15;
newElement.style.top = 35;
元素的原点是其左上角。
布局系统为每个元素计算 VisualElement.layout
属性 (type Rect
),其中包括元素的最终位置。这会考虑元素的相对或绝对位置。
layout.position
以点 (point) 表示,相对于其父级的坐标空间。
每个 VisualElement
都有一个变换属性 (ITransform
),您可以使用它为元素的位置和旋转添加额外的局部偏移。偏移量并不在计算的布局属性中表示。默认情况下,transform
是标识。
使用 worldBound
属性检索 VisualElement
的最终窗口空间坐标,同时考虑布局位置和变换。此位置包括窗口标题的高度。
以下代码示例演示了相对定位和绝对定位之间的区别。它使用自动布局系统将一些方框添加到窗口并计算它们的位置。一个方框演示 25 px
的相对偏移,而另一个方框演示 25 px, 25 px
的绝对位置。
要查看此示例运行效果,请执行以下操作:
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class PositioningTestWindow : EditorWindow
{
[MenuItem("Window/UI Toolkit/Positioning Test Window")]
public static void ShowExample()
{
var wnd = GetWindow<PositioningTestWindow>();
wnd.titleContent = new GUIContent("Positioning Test Window");
}
public void CreateGUI()
{
for (int i = 0; i < 2; i++)
{
var temp = new VisualElement();
temp.style.width = 70;
temp.style.height = 70;
temp.style.marginBottom = 2;
temp.style.backgroundColor = Color.gray;
rootVisualElement.Add(temp);
}
// 相对定位
var relative = new Label("Relative\nPos\n25, 0");
relative.style.width = 70;
relative.style.height = 70;
relative.style.left = 25;
relative.style.marginBottom = 2;
relative.style.backgroundColor = new Color(0.2165094f, 0, 0.254717f);
rootVisualElement.Add(relative);
for (int i = 0; i < 2; i++)
{
var temp = new VisualElement();
temp.style.width = 70;
temp.style.height = 70;
temp.style.marginBottom = 2;
temp.style.backgroundColor = Color.gray;
rootVisualElement.Add(temp);
}
// 绝对定位
var absolutePositionElement = new Label("Absolute\nPos\n25, 25");
absolutePositionElement.style.position = Position.Absolute;
absolutePositionElement.style.top = 25;
absolutePositionElement.style.left = 25;
absolutePositionElement.style.width = 70;
absolutePositionElement.style.height = 70;
absolutePositionElement.style.backgroundColor = Color.black;
rootVisualElement.Add(absolutePositionElement);
}
}
VisualElement.layout.position
和 VisualElement.transform
属性用于定义如何在局部坐标系和父坐标系之间进行转换。
VisualElementExtensions 静态类提供了以下扩展方法在坐标系之间转换点和矩形:
WorldToLocal
将 Vector2
或 Rect
从 Panel
空间转换为元素内的参照。LocalToWorld
将 Vector2
或 Rect
转换为 Panel
空间参照。ChangeCoordinatesTo
将 Vector2
或 Rect
从一个元素的局部空间转换为另一个元素的局部空间。