Version: 2020.3
布局引擎
编写 UXML 模板

UXML 格式

Unity Extensible Markup Language (UXML) files are text files that define the structure of the user interface. The UXML format is inspired by HTML, XAML, and XML. If you’ve worked with these formats before, you’ll find similarities with UXML. However, the UXML format includes small differences to provide an efficient way to work with Unity.

本节介绍 Unity 支持的 UXML 格式,并提供有关编写、加载和定义 UXML 模板的详细信息。还包含有关定义新元素以及如何使用 UQuery 的信息。

UXML makes it easier for less technical users to build a user interface within Unity. In UXML you can:

  • 以 XML 定义用户界面 (UI) 的结构。
  • 使用 USS 样式表来定义 UI 布局。

因此,开发者可以将精力集中于执行一些技术性工作,例如导入资源、定义逻辑和处理数据。

Defining new elements

With UI Toolkit, you can define your own user interface components and elements.

在使用 UXML 文件来定义新元素之前,必须从 VisualElement 或其子类之一派生一个新类,然后在此新类中实现相应功能。新类必须实现默认构造函数。

例如,以下代码派生新的 StatusBar 类并实现其默认构造函数:

class StatusBar : VisualElement
{
    public StatusBar()
    {
        m_Status = String.Empty;
    }

    string m_Status;
    public string status { get; set; }
}

为了使 UI 工具包在读取 UXML 文件时实例化一个新类,必须为该类定义一个工厂。除非所需的工厂需要完成一些特殊任务,否则可以从 UxmlFactoy<T> 派生该工厂。建议将工厂类放在组件类中。

例如,以下代码演示了如何通过从 UxmlFactory<T> 派生 StatusBar 类的工厂来为该类定义工厂。该工厂名为 UxmlFactory

class StatusBar : VisualElement
{
    public new class UxmlFactory : UxmlFactory<StatusBar> {}

    // ...
}

定义该工厂后,就可以在 UXML 文件中使用 <StatusBar> 元素。

定义元素的属性

可为新类定义 UXML 特征,并将其工厂设置为使用这些特征。

For example, the following code demonstrates how to define a UXML traits class to initialize the status property as a property of the StatusBar class. The status property initializes from XML data.

class StatusBar : VisualElement
{
    public new class UxmlFactory : UxmlFactory<StatusBar, UxmlTraits> {}

    public new class UxmlTraits : VisualElement.UxmlTraits
    {
        UxmlStringAttributeDescription m_Status = new UxmlStringAttributeDescription { name = "status" };

        public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
        {
            get { yield break; }
        }

        public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
        {
            base.Init(ve, bag, cc);
            ((StatusBar)ve).status = m_Status.GetValueFromBag(bag, cc);
        }
    }

    // ...
}

UxmlTraits 有两个作用:

  • The factory uses it to initialize the newly created object.
  • The schema generation process analyzes it to get information about the element. This information translates into XML schema directives.

以上代码示例执行以下操作:

  • m_Status 的声明定义一个名为 status 的 XML 属性。
  • uxmlChildElementsDescription 返回空的 IEnumerable(表明 StatusBar 元素没有子项)。
  • Init() 成员从 XML 解析器读取属性包中的 status 属性值,并将 StatusBar.status 属性设置为此值。
  • UxmlTraits 类置于 StatusBar 类中,让 Init() 方法可以访问 StatusBar 的私有成员。
  • 新的 UxmlTraits 类继承自基类 UxmlTraits,因此其共享基类的属性。
  • Init() 调用 base.Init() 来初始化基类属性。

以上代码示例使用 UxmlStringAttributeDescription 类声明了一个字符串属性。UI 工具包支持以下类型的属性,每个属性都将 C# 类型链接到 XML 类型:

属性 属性值
UxmlStringAttributeDescription 字符串
UxmlFloatAttributeDescription C# float 类型范围内的单精度浮点值。
UxmlDoubleAttributeDescription C# double 类型范围内的双精度浮点值。
UxmlIntAttributeDescription C# int 类型范围内的整数值。
UxmlLongAttributeDescription C# long 类型范围内的长整数值。
UxmlBoolAttributeDescription truefalse
UxmlColorAttributeDescription 表示颜色的字符串 (#FFFFFF)
UxmlEnumAttributeDescription<T> 表示 Enum 类型 T 的值之一的字符串。

在以上示例中,uxmlChildElementsDescription 返回一个空的 IEnumerable,表示 StatusBar 元素不接受子项。

To have an element accept children of any type, you must override the uxmlChildElementsDescription property. For example, for the StatusBar element to accept children of any type, the uxmlChildElementsDescription property must be specified as follows:

public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
    get
    {
        yield return new UxmlChildElementDescription(typeof(VisualElement));
    }
}

定义命名空间前缀

一旦在 C# 中定义了新元素,就可以开始在 UXML 文件中使用该元素。如果在新命名空间中定义新元素,应该为此命名空间定义一个前缀。命名空间前缀声明为根元素 <UXML> 的属性,并在限定元素范围时替换命名空间的全名。

要定义命名空间前缀,请将 UxmlNamespacePrefix 属性添加到要定义的每个命名空间前缀的程序集。


[assembly: UxmlNamespacePrefix("My.First.Namespace", "first")]
[assembly: UxmlNamespacePrefix("My.Second.Namespace", "second")]

You can do this at the root level (outside any namespace) of any C# file of the assembly.

架构生成系统会执行以下操作:

  • Checks for these attributes and uses them to generate the schema.
  • Adds the namespace prefix definition as an attribute of the <UXML> element in newly created UXML files
  • Includes the schema file location for the namespace in its xsi:schemaLocation attribute.

You should update the UXML schema of your project. Select Assets > Update UXML Schema to ensure that your text editor recognizes the new element.

The defined prefix is available in the newly created UXML by selecting Create > UI Toolkit > Editor Window in the Project/Assets/Editor folder.

高级用法

自定义 UXML 名称

若要自定义 UXML 名称,可覆盖其 IUxmlFactory.uxmlNameIUXmlFactory.uxmlQualifiedName 属性。确保 uxmlName 在命名空间内具有唯一性且 uxmlQualifiedName 在项目中具有唯一性。

If both names aren’t unique, an exception is thrown when you attempt to load your assembly.

以下代码示例演示了如何覆盖和自定义 UXML 名称:

public class FactoryWithCustomName : UxmlFactory<..., ...>
{
    public override string uxmlName
    {
        get { return "UniqueName"; }
    }

    public override string uxmlQualifiedName
    {
        get { return uxmlNamespace + "." + uxmlName; }
    }
}

为元素选择工厂

默认情况下,IUxmlFactory 会实例化一个元素并使用该元素的名称来选择该元素。

可以通过覆盖 IUXmlFactory.AcceptsAttributeBag 使选择过程考虑元素上的属性值。然后,工厂将检查元素属性以决定是否可以为 UXML 元素实例化对象。

This is useful if your VisualElement class is generic. In this case, the class factory for a specialization of your class could examine the value of a XML type attribute. Depending on the value, instantiation can be accepted or refused. For an example, see the implemenatation of PropertyControl<T>.

如果有多个工厂可以实例化元素,则会选择第一个已注册的工厂。

覆盖基类属性的默认值

若要更改基类中声明的属性的默认值,可在派生的 UxmlTraits 类中设置该属性的 defaultValue

例如,以下代码演示了如何更改 m_TabIndex 的默认值:

class MyElementTraits : VisualElement.UxmlTraits
    {
        public MyElementTraits()
        {
            m_TabIndex.defaultValue = 0;
        }
    }

接受任何属性

默认情况下,生成的 XML 架构会声明元素可以具有任何属性。

属性值(在 UxmlTraits 类中声明的属性除外)不受限制。这一点与 XML 验证器相反,后者会检查已声明属性的值是否与其声明匹配。

Additional attributes are included in the IUxmlAttributes bag that’s passed to the IUxmlFactory.AcceptsAttributBag() and IUxmlFactory.Init() functions. It’s up to the factory implementation to use these additional attributes. The default behavior is to discard additional attributes.

This means that these additional attributes aren’t attached to the instantiated VisualElement and you cannot query these attributes with UQuery.

定义新元素时,可以通过在 UxmlTraits 构造函数中将 UxmlTraits.canHaveAnyAttribute 属性设置为 false,将接受的属性限制为显式声明的属性。

Using Schema definitions

架构定义文件用于指定属性以及每个 UXML 元素可以包含的子元素。请将架构定义文件用作编写正确文档和验证文档的指南。

In the UXML template file, the xsi:noNamespaceSchemaLocation and xsi:schemaLocation attributes of the <UXML> root element specify the location of the schema definition files.

Select Assets > Create > UI Toolkit > Editor Window to automatically update your schema definition with the latest information from the VisualElement sub-classes used by your project. To force an update of the UXML schema files, select Assets > Update UXML Schema.

Note: Some text editors don’t recognize the xsi:noNamespaceSchemaLocation attribute. If your text editor can’t find the schema definition files, you should also specify the xsi:schemaLocation attribute.

布局引擎
编写 UXML 模板