版本:Unity 6 (6000.0)
语言:English
创建复杂的列表视图
在滚动视图中包装内容

运行时创建列表视图 UI

版本: 2022.3+

此示例演示如何运行时创建列表视图UI(用户界面) 允许用户与您的应用程序交互。Unity 目前支持三种 UI 系统。 更多信息
参见 术语表
。此示例直接使用 UXML 和 USS 文件来创建 UI 的结构和样式。如果您不熟悉 UI 工具包并希望使用 UI Builder 创建 UI,请参阅 使用 UI Builder 创建示例 UI

示例概述

此示例创建了一个简单的角色选择屏幕。当您从左侧列表中点击角色名称时,角色的详细信息将显示在右侧。

Final view of the runtime UI
运行时 UI 的最终视图

您可以在此 GitHub 存储库 中找到此示例创建的完整文件。

先决条件

本指南适用于熟悉 Unity 编辑器、UI 工具包和 C# 脚本的开发人员。在开始之前,请熟悉以下内容

创建主 UI 文档

创建主视图 UI 文档和一个 USS 文件来设置视觉元素视觉树的一个节点,它实例化或派生自 C# VisualElement 类。您可以设置外观样式、定义行为并在屏幕上将其显示为 UI 的一部分。 更多信息
参见 术语表
的样式。在 UI 文档中添加两个视觉元素作为容器:一个包含角色名称列表,另一个包含所选角色的详细信息。

The UI layout setup for the main view
主视图的 UI 布局设置
  1. 在 Unity 中使用任何模板创建一个项目。

  2. 项目窗口显示 Assets 文件夹内容的窗口(项目选项卡) 更多信息
    参见 术语表
    中,创建一个名为 UI 的文件夹来存储所有 UI 文档和样式表文件。

  3. UI 文件夹中,创建一个名为 MainView.uxml 的 UI 文档,其内容如下

    <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
        <Style src="MainView.uss" />
        <ui:VisualElement name="background">
            <ui:VisualElement name="main-container">
                <ui:ListView focusable="true" name="character-list" />
                <ui:VisualElement name="right-container">
                    <ui:VisualElement name="details-container">
                        <ui:VisualElement name="details">
                            <ui:VisualElement name="character-portrait" />
                        </ui:VisualElement>
                        <ui:Label text="Label" name="character-name" />
                        <ui:Label text="Label" display-tooltip-when-elided="true" name="character-class" />
                    </ui:VisualElement>
                </ui:VisualElement>
            </ui:VisualElement>
        </ui:VisualElement>
    </ui:UXML>
    
  4. UI 文件夹中,创建一个名为 MainView.uss 的 USS 样式表,其内容如下

#background {
    flex-grow: 1;
    align-items: center;
    justify-content: center;
    background-color: rgb(115, 37, 38);
}

#main-container {
    flex-direction: row;
    height: 350px;
}

#character-list {
    width: 230px;
    border-color: rgb(49, 26, 17);
    border-width: 4px;
    background-color: rgb(110, 57, 37);
    border-radius: 15px;
    margin-right: 6px;
}

#character-name {
    -unity-font-style: bold;
    font-size: 18px;
}

#character-class {
    margin-top: 2px;
    margin-bottom: 8px;
    padding-top: 0;
    padding-bottom: 0;
}

#right-container {
    justify-content: space-between; 
    align-items: flex-end;
}

#details-container {
    align-items: center; 
    background-color: rgb(170, 89, 57); 
    border-width: 4px; 
    border-color: rgb(49, 26, 17);
    border-radius: 15px;
    width: 252px; 
    justify-content: center; 
    padding: 8px;
    height: 163px;
}

#details {
    border-color: rgb(49, 26, 17); 
    border-width: 2px; 
    height: 120px; 
    width: 120px; 
    border-radius: 13px; 
    padding: 4px;
    background-color: rgb(255, 133, 84);
}

#character-portrait {
    flex-grow: 1; 
    -unity-background-scale-mode: scale-to-fit;
}

.unity-collection-view__item {
    justify-content: center;
}

创建列表条目 UI 文档

为列表中的各个条目创建 UI 文档和样式表。角色列表条目包括一个彩色背景框架和角色的名称。

List entry that shows a characters name
显示角色名称的列表条目
  1. UI 文件夹中,创建一个名为 ListEntry.uxml 的 UI 文档,其内容如下

    <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="ListEntry.uss" />
    <ui:VisualElement name="list-entry">
        <ui:Label text="Label" display-tooltip-when-elided="true" name="character-name" />
    </ui:VisualElement>
    </ui:UXML>
    
  2. UI 文件夹中,创建一个名为 ListEntry.uss 的样式表文件,其内容如下

#list-entry {
    height: 41px;
    align-items: flex-start;
    justify-content: center;
    padding-left: 10px;
    background-color: rgb(170, 89, 57);
    border-color: rgb(49, 26, 17);
    border-width: 2px;
    border-radius: 15px;
}

#character-name {
    -unity-font-style: bold;
    font-size: 18px;
    color: rgb(49, 26, 17);
}

创建要显示的示例数据

创建示例数据以填充 UI 中的角色列表。对于角色列表,创建一个类来保存角色名称、类别和肖像图像。

  1. 在 Asset 文件夹中,创建一个名为 Scripts 的文件夹来存储您的 C#脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间推移修改组件属性并以任何您喜欢的方式响应用户输入。 更多信息
    参见 术语表

  2. Scripts 文件夹中,创建一个名为 CharacterData.cs 的 C# 脚本,其内容如下

    using UnityEngine;
        
    public enum ECharacterClass
    {
        Knight, Ranger, Wizard
    }
        
    [CreateAssetMenu]
    public class CharacterData : ScriptableObject
    {
        public string CharacterName;
        public ECharacterClass Class;
        public Sprite PortraitImage;
    }
    

    这将在 Assets > Create 菜单中创建 角色数据项。

  3. 在 Assets 文件夹中,创建一个名为 Resources 的文件夹。

  4. Resources 文件夹中,创建一个名为 Characters 的文件夹来存储所有示例角色数据。

  5. Characters 文件夹中,右键单击并选择 Create > Character Data 以创建 ScriptableObject 的实例。

  6. 创建更多 CharacterData 实例并使用占位符数据填充它们。

设置场景

在 SampleScene 中创建一个 UIDocument游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等等。游戏对象的功能由附加到它的组件定义。 更多信息
参见 术语表
,并将 UI 文档作为源资产添加。

  1. 在 SampleScene 中,选择 GameObject > UI Toolkit > UI Document
  2. 在 Hierarchy 窗口中选择 UIDocument 游戏对象。
  3. MainView.uxml 从项目窗口拖动到 检查器一个 Unity 窗口,显示有关当前选定游戏对象、资产或项目设置的信息,允许您检查和编辑值。 更多信息
    参见 术语表
    窗口中 UI Document 组件的 Source Asset 字段。这将源资产引用到 UXML 文件。

为列表条目和主视图创建控制器

创建两个具有以下类的 C# 脚本

  • 一个 CharacterListEntryController 类,用于在列表条目的 UI 中显示角色实例的数据。它需要访问角色名称的标签并将其设置为显示给定角色实例的名称。
  • 一个 CharacterListController 类,用于主视图中的角色列表,以及一个 MonoBehaviour 脚本,它实例化并将其分配给视觉树一个对象图,由轻量级节点组成,它保存窗口或面板中的所有元素。它定义了您使用 UI 工具包构建的每个 UI。
    参见 术语表

注意CharacterListEntryController 类不是 MonoBehaviour。由于 UI 工具包中的视觉元素不是游戏对象,因此您无法将组件附加到它们。相反,您将类附加到 CharacterListController 类中的 userData 属性。

  1. Scripts 文件夹中,创建一个名为 CharacterListEntryController.cs 的 C# 脚本,其内容如下

    using UnityEngine.UIElements;
        
    public class CharacterListEntryController
    {
        Label m_NameLabel;
        
        // This function retrieves a reference to the 
        // character name label inside the UI element.
        public void SetVisualElement(VisualElement visualElement)
        {
            m_NameLabel = visualElement.Q<Label>("character-name");
        }
        
        // This function receives the character whose name this list 
        // element is supposed to display. Since the elements list 
        // in a `ListView` are pooled and reused, it's necessary to 
        // have a `Set` function to change which character's data to display.
        public void SetCharacterData(CharacterData characterData)
        {
            m_NameLabel.text = characterData.CharacterName;
        }
    }
    
  2. Scripts 文件夹中,创建一个名为 CharacterListController.cs 的 C# 脚本,其内容如下

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UIElements;
        
    public class CharacterListController
    {
        // UXML template for list entries
        VisualTreeAsset m_ListEntryTemplate;
        
        // UI element references
        ListView m_CharacterList;
        Label m_CharClassLabel;
        Label m_CharNameLabel;
        VisualElement m_CharPortrait;
        
        List<CharacterData> m_AllCharacters;
        
        public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
        {
            EnumerateAllCharacters();
        
            // Store a reference to the template for the list entries
            m_ListEntryTemplate = listElementTemplate;
        
            // Store a reference to the character list element
            m_CharacterList = root.Q<ListView>("character-list");
        
            // Store references to the selected character info elements
            m_CharClassLabel = root.Q<Label>("character-class");
            m_CharNameLabel = root.Q<Label>("character-name");
            m_CharPortrait = root.Q<VisualElement>("character-portrait");
        
            FillCharacterList();
        
            // Register to get a callback when an item is selected
            m_CharacterList.selectionChanged += OnCharacterSelected;
        }
        
        void EnumerateAllCharacters()
        {
            m_AllCharacters = new List<CharacterData>();
            m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
        }
        
        void FillCharacterList()
        {
            // Set up a make item function for a list entry
            m_CharacterList.makeItem = () =>
            {
                // Instantiate the UXML template for the entry
                var newListEntry = m_ListEntryTemplate.Instantiate();
        
                // Instantiate a controller for the data
                var newListEntryLogic = new CharacterListEntryController();
        
                // Assign the controller script to the visual element
                newListEntry.userData = newListEntryLogic;
        
                // Initialize the controller script
                newListEntryLogic.SetVisualElement(newListEntry);
        
                // Return the root of the instantiated visual tree
                return newListEntry;
            };
        
            // Set up bind function for a specific list entry
            m_CharacterList.bindItem = (item, index) =>
            {
                (item.userData as CharacterListEntryController)?.SetCharacterData(m_AllCharacters[index]);
            };
        
            // Set a fixed item height matching the height of the item provided in makeItem. 
            // For dynamic height, see the virtualizationMethod property.
            m_CharacterList.fixedItemHeight = 45;
        
            // Set the actual item's source list/array
            m_CharacterList.itemsSource = m_AllCharacters;
        }
        
        void OnCharacterSelected(IEnumerable<object> selectedItems)
        {
            // Get the currently selected item directly from the ListView
            var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
        
            // Handle none-selection (Escape to deselect everything)
            if (selectedCharacter == null)
            {
                // Clear
                m_CharClassLabel.text = "";
                m_CharNameLabel.text = "";
                m_CharPortrait.style.backgroundImage = null;
        
                return;
            }
        
            // Fill in character details
            m_CharClassLabel.text = selectedCharacter.Class.ToString();
            m_CharNameLabel.text = selectedCharacter.CharacterName;
            m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.PortraitImage);
        }
    }
    

将控制器脚本附加到主视图

CharacterListController 不是 MonoBehaviour,因此您无法将其直接附加到游戏对象。为了克服这个问题,创建一个 MonoBehaviour 脚本并将其附加到与 UIDocument 相同的游戏对象。在此脚本中,您不需要实例化 MainView.uxml,因为它已由 UIDocument 组件实例化。相反,访问 UIDocument 组件以获取已实例化视觉树的引用。然后,创建 CharacterListController 的实例并传入视觉树的根元素以及用于单个列表元素的 UXML 模板。

注意:当 UI 重新加载时,包含 UIDocument 组件的相同游戏对象上的任何关联的 MonoBehaviour 组件在重新加载之前会被禁用,然后在重新加载后重新启用。因此,您必须将与 UI 相关的代码放在此 MonoBehaviourOnEnableOnDisable 方法中。有关更多信息,请参阅 在游戏视图中渲染 UI

  1. Scripts 文件夹中,创建一个名为 MainView.cs 的 C# 脚本,其内容如下

    using UnityEngine;
    using UnityEngine.UIElements;
        
    public class MainView : MonoBehaviour
    {
        [SerializeField]
        VisualTreeAsset m_ListEntryTemplate;
        
        void OnEnable()
        {
            // The UXML is already instantiated by the UIDocument component
            var uiDocument = GetComponent<UIDocument>();
        
            // Initialize the character list controller
            var characterListController = new CharacterListController();
            characterListController.InitializeCharacterList(uiDocument.rootVisualElement, m_ListEntryTemplate);
        }
    }
    
  2. 在 SampleScene 中,选择 UIDocument

  3. MainView.cs 拖到检查器窗口中的 添加组件

  4. ListEntry.uxml 拖到 ListEntry Template 字段。

  5. 进入播放模式以查看在游戏视图中显示的 UI。

其他资源

创建复杂的列表视图
在滚动视图中包装内容