UI Toolkit簡介 中介紹了樣式屬性,UI Toolkit容器 和 UI Toolkit元素 中介紹了容器和元素,本文將介紹樣式選擇器(Selector),主要包含樣式類選擇器(Class Selector)、C# 類選擇器(Type Selector)、名稱選擇器(Name Selector)、通用選擇器(Universal Selector)、後代選擇器(Descendant Selector)、子選擇器(Child Selector)、多重選擇器(Multiple Selector)、偽類選擇器(Pseudo Class)等。樣式選擇器官方介紹見→USS selectors。
簡單選擇器優先順序:
// * < C# Type < .Name < #Name
// 通用選擇器 < C#類選擇器 < 樣式類選擇器 < 名稱選擇器
Universal Selector < Type Selector < Class Selector < Name Selector
Class 選擇器是命名以 "." 號開頭的選擇器(如:.red、.abc、.xyz 等),需要手動繫結到元素上,官方介紹見→Class selectors。
1)建立 UI
在 UI Builder 的 Hierarchy 視窗中建立 UI 如下。
顯示如下。
2)建立 USS 檔案
在 StyleSheets 視窗點選 "+" 號,選擇 Create New USS 選項,如下。選擇 USS 檔案儲存路徑,命名為 StyleSelectorDemo。
3)建立選擇器
在 StyleSheets 視窗的 selector 框中輸入 ".textColor",如下。注意:textColor 前面有個點,命名可以隨意,如:demo、test 等都行。
按 Enter 鍵後,建立了 selector,如下。
4)修改選擇器的屬性
選中 ".textColor" 選擇器,在 Inspector 視窗修改文字顏色,如下。
5)繫結選擇器到元素
將 ".textColor" 選擇器拖拽到 Hierarchy 或 Viewport 視窗中相關元素上,實現樣式選擇器與元素的繫結。
也可以通過以下方式繫結元素:選中元素後,在 Inspector 視窗的 Style Class List 中新增 selector,如下。
選擇器繫結到元素後,在 Inspector 視窗可以看到元素繫結的選擇器,如下。可以通過後面的 "x" 號刪除選擇器。
6)顯示效果
Label 和 Button 都手動繫結了 ".textColor" 選擇器,顯示效果如下。
Type 選擇器是以元素的 C# 類名命名的選擇器(如:Label、Button、VisualElement 等),會自動繫結到對應 Type 的元素上,不需要手動繫結,官方介紹見→Type selectors。
1)建立選擇器
在 StyleSheets 視窗建立名為 "Label" 的選擇器,如下。
2)修改選擇器的屬性
選中 "Label" 選擇器,在 Inspector 視窗修改文字樣式為斜體,如下。
3)顯示效果
Label 元素會自動繫結 "Label" 選擇器,Button 元素則不會,即使手動將 "Label" 選擇器拖拽到 Button 元素上,也不會繫結成功。顯示效果如下。
Name 選擇器是命名以 "#" 號開頭的選擇器(如:#Name、#Abc、#Xyz 等),會自動繫結到對應 Name 的元素上,不需要手動繫結,官方介紹見→Name selectors。
1)修改元素名
新建立的元素預設元素名為空,修改元素名如下。
2)建立選擇器
在 StyleSheets 視窗建立名為 "Button1" 的選擇器,如下。
3)修改選擇器的屬性
選中 "Button1" 選擇器,在 Inspector 視窗修改背景顏色,如下。
4)顯示效果
Button1 元素會自動繫結 "Button1" 選擇器,Label1 元素則不會,即使手動將 "Button1" 選擇器拖拽到 Label1 元素上,也不會繫結成功。顯示效果如下。
Universal 選擇器是以 "*" 號命名的選擇器,會自動繫結到所有元素上,不需要手動繫結,官方介紹見→Universal selectors。
1)建立選擇器
在 StyleSheets 視窗建立名為 "*" 的選擇器,如下。
2)修改選擇器的屬性
選中 "*" 選擇器,在 Inspector 視窗修改邊框寬度和顏色,如下。
3)顯示效果
所有元素都會自動繫結 "*" 選擇器,顯示效果如下。
複雜選擇器是指由多個簡單選擇器按照特定規則組合而成的選擇器。
Descendant 選擇器由多個簡單選擇器通過空格連線而成,它匹配的是某個 UI 元素底下符合規則的所有層級的子元素,官方介紹見→Descendant selectors。
// 在selector1匹配的子元素中匹配selector2, ...
// 選擇器的順序不同匹配的元素也不同
selector1 selector2 {...}
1)UI 搭建
UI 層級結構如下,其中 Background、VE1、VE2、VE3 都是 VisualElement,Label1、Label2、Label3 都是 Label。
建立以下簡單選擇器。其中,Lable 選擇器中修改了字型大小為 30;.red 選擇器中修改了背景顏色為紅色,並繫結到 VE1,.green 選擇器中修改了背景顏色為綠色,並繫結到 VE2;.blue 選擇器中修改了背景顏色為藍色,並繫結到 VE3。
UI 顯示如下。
2)建立 Descendant 選擇器
在 StyleSheets 視窗建立以下選擇器。
3)顯示效果
可以看到,"#VE1 Label" 選擇器匹配的是 Label1 元素,".green Label" 選擇器匹配的是 Label2 元素,"#Background *" 選擇器匹配的是 VE1、VE2、VE3、Label1、Label2、Label3 元素。
Child 選擇器由多個簡單選擇器通過 " > " 連線而成,它匹配的是某個 UI 元素底下符合規則的第一層級的子元素,官方介紹見→Child selectors。
// 在selector1匹配的子元素中匹配selector2, ...
// 選擇器的順序不同匹配的元素也不同
selector1 > selector2 {...}
1)UI 搭建
同 3.1 1)節。
2)建立 Child 選擇器
在 StyleSheets 視窗建立以下選擇器。
3)顯示效果
可以看到,"#VE1 > Label" 選擇器匹配的是 Label1 元素,".green > Label" 選擇器匹配的是 Label2 元素,"#Background > *" 選擇器匹配的是 VE1、VE2、VE3 元素。
Multiple 選擇器由多個簡單選擇器直接而成,它匹配的是符合所有規則的元素,官方介紹見→Multiple selectors。
// 匹配同時滿足selector1、selector2、...的元素
// 選擇器的順序不同匹配的元素相同
selector1selector2 {...}
說明:Class 選擇器可以通過 "." 區分,Name 選擇器可以通過 "#" 區分,Type 選擇器沒有區分符號,因此,一個多重選擇器中最多允許有一個 Type 選擇器,並且必須放在第一位。
1)UI 搭建
同 3.1 1)節。
2)建立 Multiple 選擇器
在 StyleSheets 視窗建立以下選擇器。
3)顯示效果
可以看到,"Label#Label1" 選擇器匹配的是 Label1 元素,"VisualElement.green" 選擇器匹配的是 VE2 元素,"Label*" 選擇器匹配的是 Label1、Label1、Label3 元素。
Pseudo 選擇器是指由簡單選擇器和狀態符連線而成,它匹配的是特定狀態下的元素,官方介紹見→Pseudo-classes。
// 匹配滿足selector且進入state狀態的元素
selector:state {...}
狀態符主要以下幾種。
1)UI 搭建
同 3.1 1)節。
2)建立 Pseudo 選擇器
在 StyleSheets 視窗建立以下選擇器。
3)顯示效果
可以看到,"#Label1:hover" 選擇器匹配的是進入 hover 狀態的 Label1 元素,".green:hover" 選擇器匹配的是進入 hover 狀態的 VE2 元素,"*:hover" 選擇器匹配的是進入 hover 狀態的 VE1、VE2、VE3、Label1、Label1、Label3 元素。
1)樣式選擇器的新增和刪除
// 如果visualElement有StyleName樣式, 就將其刪除; 如果visualElement沒有StyleName樣式, 就將其新增
visualElement.ToggleInClassList("StyleName"); // StyleName是樣式選擇器名, 不需要前面的"."
說明:只能新增和刪除樣式類選擇器(以 "." 命名開頭的選擇器),其他選擇器都是自動繫結到元素上的。
2)uss 樣式檔案替換
VisualElement root = GetComponent<UIDocument>().rootVisualElement; // 根容器
VisualElementStyleSheetSet styleSheetSet = root.styleSheets; // 樣式集合
styleSheetSet.Remove(oldStyleSheet); // 移除舊樣式, oldStyleSheet是StyleSheet型別變數, 指向一個uss檔案
styleSheetSet.Add(newlightTheme); // 新增新樣式, newStyleSheet是StyleSheet型別變數, 指向一個uss檔案
本節將實現亮主題和暗主題的切換,完整資源見→Unity3D切換樣式主題。
Page.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<Style src="project://database/Assets/SwitchTheme/StyleSheets/LightTheme.uss?fileID=7433441132597879392&guid=b86be23b06b471b43a7ea453aed7df74&type=3#LightTheme" />
<ui:VisualElement name="Background" class="bgColor" style="flex-direction: row; flex-grow: 1; background-image: url('project://database/Assets/SwitchTheme/Textures/Background_Sky.png?fileID=2800000&guid=49e1c76bfa6ef0546a39fd2bceb69b9a&type=3#Background_Sky');">
<ui:VisualElement name="Left" style="flex-grow: 0; border-right-width: 2px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0); flex-basis: 30%;">
<ui:VisualElement name="Top" class="dark top" style="flex-basis: 30%; margin-bottom: 0; border-bottom-width: 2px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" />
<ui:VisualElement name="Middle" style="flex-basis: auto; flex-grow: 1;">
<ui:Label text="Label" display-tooltip-when-elided="true" class="item middleBg middle" style="flex-grow: 1; -unity-text-align: middle-center; font-size: 60px; flex-shrink: 1; margin-top: 5px; margin-bottom: 5px;" />
<ui:Label text="Theme" display-tooltip-when-elided="true" name="Theme" class="item middle" style="flex-grow: 1; -unity-text-align: middle-center; font-size: 60px; flex-shrink: 1; margin-top: 5px; margin-bottom: 5px;" />
<ui:RadioButtonGroup value="-1" choices="Light,Drak" name="SwitchTheme" class="middle hide" style="flex-grow: 0; font-size: 45px; -unity-text-align: middle-center; align-items: center; justify-content: center; flex-direction: column; margin-left: 0; margin-right: 0; margin-top: 5px; margin-bottom: 5px; flex-shrink: 1;" />
<ui:Label text="Label" display-tooltip-when-elided="true" class="item middle" style="flex-grow: 1; -unity-text-align: middle-center; font-size: 60px; flex-shrink: 1; margin-top: 5px; margin-bottom: 5px;" />
</ui:VisualElement>
<ui:VisualElement name="Bottom" style="flex-basis: 25%; border-top-width: 2px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);">
<ui:Label text="Bottom" display-tooltip-when-elided="true" style="flex-shrink: 1; flex-grow: 1; font-size: 100px; -unity-text-align: middle-center;" />
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement name="Right" style="flex-grow: 0; flex-basis: 70%; align-items: center; justify-content: center;">
<ui:Label text="Work Space" display-tooltip-when-elided="true" class="right" />
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
LightTheme.uss
Label {
color: rgb(48, 47, 47);
}
RadioButton:hover {
background-color: rgb(217, 217, 217);
}
.unity-radio-button__checkmark-background {
margin-right: 10px;
width: 30px;
height: 30px;
justify-content: center;
align-items: center;
border-left-width: 1px;
border-right-width: 1px;
border-top-width: 1px;
border-bottom-width: 1px;
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
background-color: rgb(43, 43, 43);
border-left-color: rgba(248, 242, 242, 255);
border-right-color: rgba(248, 242, 242, 255);
border-top-color: rgba(248, 242, 242, 255);
border-bottom-color: rgba(248, 242, 242, 255);
padding-top: 0;
}
.unity-radio-button__checkmark {
width: 15px;
height: 15px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
justify-content: center;
align-items: center;
background-color: rgb(217, 213, 213);
}
.bgColor {
-unity-background-image-tint-color: rgb(255, 255, 255);
}
.top {
background-image: url('project://database/Assets/SwitchTheme/Textures/unity_dark.png?fileID=2800000&guid=49ee81a1e2e15df46968d5d1978abc12&type=3#unity_dark');
-unity-background-scale-mode: scale-to-fit;
}
.middle {
background-color: rgba(0, 239, 255, 0.39);
}
.right {
-unity-text-align: middle-center;
-unity-font-style: bold;
font-size: 200px;
color: rgb(0, 168, 255);
justify-content: center;
align-items: center;
-unity-text-outline-width: 1px;
-unity-text-outline-color: rgb(255, 31, 0);
text-shadow: 5px 2px 1px rgb(255, 255, 255);
}
.hide {
display: none;
}
.item:hover {
background-color: rgb(58, 56, 56);
color: rgb(219, 216, 216);
}
.middle {
background-color: rgba(0, 239, 255, 0.39);
}
DarkTheme.uss
Label {
color: rgb(212, 212, 212);
}
RadioButton:hover {
background-color: rgb(38, 38, 38);
}
.unity-radio-button__checkmark-background {
margin-right: 10px;
width: 30px;
height: 30px;
justify-content: center;
align-items: center;
border-left-width: 1px;
border-right-width: 1px;
border-top-width: 1px;
border-bottom-width: 1px;
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
background-color: rgb(255, 255, 255);
padding-top: 0;
}
.unity-radio-button__checkmark {
width: 15px;
height: 15px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
justify-content: center;
align-items: center;
background-color: rgb(51, 50, 50);
}
.bgColor {
-unity-background-image-tint-color: rgb(132, 132, 132);
}
.top {
background-image: url('project://database/Assets/SwitchTheme/Textures/unity_light.png?fileID=2800000&guid=92a215376eed0c9498000506d15b4afe&type=3#unity_light');
-unity-background-scale-mode: scale-to-fit;
}
.middle {
background-color: rgba(46, 44, 44, 0.39);
}
.right {
-unity-text-align: middle-center;
-unity-font-style: bold;
font-size: 200px;
color: rgb(197, 194, 194);
justify-content: center;
align-items: center;
-unity-text-outline-width: 1px;
-unity-text-outline-color: rgb(255, 216, 0);
text-shadow: 5px 2px 1px rgb(53, 52, 52);
}
.hide {
display: none;
}
.item:hover {
background-color: rgb(214, 211, 211);
color: rgb(46, 45, 45);
}
SwitchTheme.cs
using UnityEngine;
using UnityEngine.UIElements;
public class SwitchTheme : MonoBehaviour {
private VisualElement root; // 根容器
private RadioButtonGroup switchThemeElement; // 切換主題元素
private VisualElementStyleSheetSet styleSheetSet; // 主題集合
[SerializeField] private StyleSheet lightTheme; // 亮主題, 指向LightTheme.uss檔案
[SerializeField] private StyleSheet darkTheme; // 暗主題, 指向DarkTheme.uss檔案
private void Awake() {
root = GetComponent<UIDocument>().rootVisualElement;
styleSheetSet = root.styleSheets;
Label themeLabel = root.Q<Label>("Theme");
themeLabel.RegisterCallback<ClickEvent>(OnClick);
switchThemeElement = root.Q<RadioButtonGroup>("SwitchTheme");
switchThemeElement.RegisterValueChangedCallback(OnValueChanged);
}
private void OnClick(ClickEvent e) { // 點選回撥函數
switchThemeElement.ToggleInClassList("hide");
}
private void OnValueChanged(ChangeEvent<int> e) { // value變化回撥函數
if (e.newValue == 0) { // 切換為亮主題
styleSheetSet.Remove(darkTheme);
styleSheetSet.Add(lightTheme);
} else { // 切換為暗主題
styleSheetSet.Remove(lightTheme);
styleSheetSet.Add(darkTheme);
}
}
}
執行效果如下。
宣告:本文轉自【Unity3D】UI Toolkit樣式選擇器。