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

UXML 格式

UXML 文件是定义用户界面逻辑结构的文本文件。UXML 文件中使用的格式受到 HTML(超文本标记语言)、XAML(可扩展应用程序标记语言)和 XML(可扩展标记语言)的启发。如果您熟悉这些公认的格式,应该会注意到 UXML 中有许多相似之处。但是,UXML 格式包含一些小差异,旨在与 Unity 高效融合。

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

UXML 使技术水平较低的用户更容易在 Unity 中构建用户界面。UXML 让您可以:

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

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

定义新元素

UIElements 具有可扩展的特点。您可以定义自己的用户界面组件和元素。

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

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

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

    string m_Status;
    public string status { get; }
}

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

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

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

    // ...
}

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

注意:工厂在 2018.2 版中得到改进。如果在以前版本中定义了工厂,应该将它们移植到 2018.3 版本以避免使用现在已过时的 API。

定义元素的属性

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

例如,以下代码演示了如何定义 UXML 特征类,从而将 status 属性初始化为 StatusBar 类的属性。应从 XML 数据初始化 status 属性。

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

    public new class Traits : base.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 有两个作用:

  • 由工厂用于初始化新建的对象。

  • 由架构生成过程对其进行分析以获取有关元素的信息。该信息将转换为 XML 架构指令。

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

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

以上代码示例使用 UxmlStringAttributeDescription 类声明了一个字符串属性。UIElements 支持以下类型的属性,每个属性都将 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 元素不接受子项。

要让元素接受任何类型的子项,必须覆盖 uxmlChildElementsDescription 属性。例如,为了让 StatusBar 元素接受任何类型的子项, 必须按如下方式指定 uxmlChildElementsDescription 属性:

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")]

此操作可以在程序集的任何 C# 文件的根级别(在任何命名空间之外)完成。

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

  • 检查是否有这些属性并使用它们来生成架构。
  • 在新建的 UXML 文件中,将命名空间前缀定义为添加 <UXML> 元素的属性。
  • 将命名空间的架构文件位置包含在其 xsi:schemaLocation 属性中。

应该更新项目的 UXML 架构。请选择 Assets > Update UIElements Schema 以确保文本编辑器识别新元素。

高级用法

自定义 UXML 名称

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

如果两个名称不唯一,则在尝试加载程序集时将抛出异常。

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

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

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

为元素选择工厂

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

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

例如,如果 VisualElement 类为泛类,这将很有用。在这种情况下,用于类特化的类工厂可以检查 XML type 属性的值。根据值的不同,可以接受或拒绝实例化。请参阅 PropertyControl<T> 的实现以查看示例。

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

覆盖基类属性的默认值

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

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

class MyElementTraits : VisualElementUxmlTraits
    {
        public Traits()
        {
            m_focusIndex.defaultValue = 0;
        }
    }

接受任何属性

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

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

其他属性包含在传递到 IUxmlFactory.AcceptsAttributBag()IUxmlFactory.Init() 函数的 IUxmlAttributes 包中。是否使用这些附加属性取决于工厂的实现。默认行为是丢弃附加属性。

这意味着这些附加属性不会附加到实例化的 VisualElement,并且不能使用 UQuery 来查询这些属性。

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

使用架构定义

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

在 UXML 模板文件中,<UXML> 根元素的 xsi:noNamespaceSchemaLocationxsi:schemaLocation 属性指定了架构定义文件所在位置。

选择 Assets > Create > UIElements View 菜单项,即可使用从项目所用的 VisualElement 子类中收集的最新信息来自动更新架构定义。要强制更新 UXML 架构文件,请选择 Assets > Update UIElements Schema

注意:某些文本编辑器无法识别 xsi:noNamespaceSchemaLocation 属性。如果文本编辑器找不到架构定义文件,您还应指定 xsi:schemaLocation 属性。


  • 2018–11–02 页面已修订并只进行了有限的编辑审查
布局引擎
编写 UXML 模板