Notifications
Article
UGUI自动布局详解
Published 23 days ago
648
3
自动布局系统提供了将元素放置在嵌套布局组(如水平组、垂直组或网格)中的方法。使用该系统还可以根据元素包含的内容自动调整元素的大小。

自动布局

矩形变换布局系统足够灵活,可以处理许多不同类型的布局,还允许以完全自由形式的方式布置元素。但是,有时可能需要更结构化的系统。
自动布局系统提供了将元素放置在嵌套布局组(如水平组、垂直组或网格)中的方法。使用该系统还可以根据元素包含的内容自动调整元素的大小。例如,按钮可动态调整大小来适合其文本内容并加上一些填充。
自动布局系统是建立在基本矩形变换布局系统之上的系统。您可以选择将其用于某些或所有元素。

了解布局控制器

布局控制器是控制一个或多个布局元素(即具有矩形变换的游戏对象)的大小和可能位置的组件。布局控制器可以控制自己的布局元素(其本身所在的同一游戏对象),或者也可控制子布局元素。用作布局控制器的组件本身也可以同时用作布局元素。

了解布局元素

自动布局系统基于布局元素和布局控制器的概念。一个布局元素是具有矩形变换以及其他可选组件的游戏对象。布局元素对自身应具有的大小有一定的了解。布局元素不直接设置自身的大小,而是可由用作布局控制器的其他组件使用布局元素提供的信息来计算布局元素要使用的大小。
布局元素具有相应的属性来定义自身的以下参数:Minimum width、Minimum height、Preferred width、Preferred height、Flexible width和Flexible height等。
使用布局元素提供的信息的布局控制器组件示例为内容大小适配器 (Content Size Fitter) 以及各种布局组 (Layout Group) 组件。布局组中的布局元素大小设置的基本原则如下:首先分配最小大小;如果有足够的可用空间,则分配偏好大小;如果有额外的可用空间,则分配灵活大小。
任何带有矩形变换的游戏对象都可以作为布局元素。默认情况下,布局元素的最小大小、偏好大小和灵活大小均为 0。某些组件在添加到游戏对象时将更改这些布局属性。
图像 (Image) 和文本 (Text) 组件是提供布局元素属性的两个组件示例。这些组件会更改偏好宽度和高度来匹配精灵或文本内容。

布局组

布局组充当布局控制器,可用于控制其子布局元素的大小和位置。例如,水平布局组将其子项并排放置,而网格布局组将其子项放在网格中。
布局组不控制自身的大小。相反,布局组自身作为布局元素,可由其他布局控制器进行控制或手动设置。
无论分配给布局组的大小如何,在大多数情况下,布局组都将尝试根据其子布局元素报告的最小大小、偏好大小和灵活大小为每个子布局元素分配适当的空间量。布局组也可通过这种方式任意嵌套。

矩形变换驱动属性

由于自动布局系统中的布局控制器可以自动控制某些 UI 元素的大小和位置,因此不应通过 Inspector 或 Scene 视图同时手动编辑这些大小和位置。无论如何,即使更改这些值,布局控制器在下一次布局计算时也会重置这些值。
矩形变换有一个驱动属性的概念可解决这一问题。例如,Horizontal Fit 属性设置为 Minimum 或 Preferred 的内容大小适配器将在同一个游戏对象上驱动矩形变换的宽度。该宽度将显示为只读状态,并且矩形变换顶部的小信息框将指示一个或多个属性由内容大小适配器驱动。
除了防止手动编辑之外,矩形变换使用驱动属性还有其他原因。只需更改 Game 视图的分辨率或大小即可更改布局。这样一来可以改变布局元素的大小或位置,进而改变驱动属性的值。但是,通常不希望仅仅因为调整了 Game 视图的大小就将场景标记为具有未保存的更改。为防止这种情况,驱动属性的值不会保存为场景的一部分,对这些属性的更改也不会将场景标记为已更改状态。

布局接口

自动布局系统内置了一些组件,但也可以创建以自定义方式控制布局的新组件。为此,应让组件实现可由自动布局系统识别的特定接口。
如果组件实现 ILayoutElement 接口,则自动布局系统会将该组件视为布局元素。如果组件实现 ILayoutGroup 接口,则该组件应该驱动其子项的矩形变换。如果组件实现 ILayoutSelfController 接口,则该组件应该驱动自己的 RectTransform。

布局计算

自动布局系统按以下顺序计算和执行布局:
1.通过在 ILayoutElement 组件上调用 CalculateLayoutInputHorizontal 来计算布局元素的最小宽度、偏好宽度和灵活宽度。此过程以自下而上的顺序执行,即子项的计算先于父项,这样父项可以在自己的计算中参考子项的信息。2.通过在 ILayoutController 组件上调用 SetLayoutHorizontal 来计算和设置布局元素的有效宽度。此过程自上而下的顺序执行,即子项的计算晚于父项,因为子项宽度的分配需要基于父项中可用的完整宽度。在此步骤之后,布局元素的矩形变换便有了新的宽度。3.通过在 ILayoutElement 组件上调用 CalculateLayoutInputVertical 来计算布局元素的最小高度、偏好高度和灵活高度。此过程以自下而上的顺序执行,即子项的计算先于父项,这样父项可以在自己的计算中参考子项的信息。4.通过在 ILayoutController 组件上调用 SetLayoutVertical 来计算和设置布局元素的有效高度。此过程自上而下的顺序执行,即子项的计算晚于父项,因为子项高度的分配需要基于父项中可用的完整高度。在此步骤之后,布局元素的矩形变换便有了新的高度。
从上面可以看出,自动布局系统首先计算宽度,然后计算高度。因此,计算的高度可取决于宽度,但计算的宽度决不能取决于高度。

触发布局重新构建

如果组件上的属性发生变化,并可能导致当前布局不再有效,则需要重新计算布局。可使用以下调用来触发重新计算:LayoutRebuilder.MarkLayoutForRebuild (transform as RectTransform);
重新构建不会立即发生,而是在当前帧结束时并在渲染之前进行。不立即执行的原因是这样做会导致布局在同一帧期间可能多次重新构建,而这对性能不利。
应按以下准则触发重新构建:
  • 在可更改布局的属性的 setter 中进行。
  • 在以下回调中进行:OnEnable、OnDisable、OnRectTransformDimensionsChange、OnValidate(仅在 Editor 中需要,在运行时不需要)、OnDidApplyAnimationProperties。

布局元素组件

如果要覆盖布局元素的最小大小、偏好大小或灵活大小,可通过向游戏对象添加布局元素组件来实现。
当布局控制器为布局元素分配宽度或高度时,按以下方式使用相应属性:
首先分配最小大小。
  • 如果有足够的可用空间,则分配偏好大小。
  • 如果有额外的可用空间,则分配灵活大小。
使用布局元素组件可以覆盖一个或多个布局属性的值。启用要覆盖的属性的复选框,然后指定要用于覆盖的值。
最小大小和偏好大小以常规单位定义,而灵活大小以相对单位定义。如果任何布局元素的灵活大小大于零,则意味着将填充所有可用空间。同级的相对灵活大小值决定了每个同级填充的可用空间的比例。最常见的情况是,灵活宽度和高度仅设置为 0 或 1。
在某些情况下,同时指定偏好大小和灵活大小是有意义的。仅在完全分配所有偏好大小后才分配灵活大小。因此,指定了灵活大小但未指定偏好大小的布局元素将保持其最小大小,直到其他布局元素已扩展到完整偏好大小为止,仅在此后才基于额外的可用空间开始扩展。通过同时指定灵活大小,可以避免这种情况,元素可与具有偏好大小的其他布局元素一起扩展到偏好大小,然后在分配完所有灵活大小后进一步扩展。

内容大小适配器 (Content Size Fitter)

内容大小适配器充当布局控制器,可用于控制其自身布局元素的大小。大小由游戏对象上布局元素组件提供的最小大小或偏好大小确定。此类布局元素可以是图像或文本组件、布局组或布局元素组件。
值得注意的是,当调整矩形变换的大小时(无论是通过内容大小适配器还是其他工具),大小调整是围绕轴心进行的。这意味着可使用轴心来控制大小调整的方向。
例如,当轴心位于中心位置时,内容大小适配器将在所有方向朝外均匀扩展矩形变换。当轴心位于左上角时,内容大小适配器将向右下方向扩展矩形变换。

宽高比适配器 (Aspect Ratio Fitter)

宽高比适配器充当布局控制器,可用于控制其自身布局元素的大小。宽高比适配器可以调整高度来适应宽度或反之,也可以使元素在其父项内部适应或包裹其父项。宽高比适配器不考虑布局信息,例如最小大小和偏好大小。
值得注意的是,当调整矩形变换的大小时(无论是通过宽高比适配器还是其他工具),大小调整是围绕轴心进行的。这意味着可使用轴心来控制矩形的对齐。例如,位于顶部中央位置的轴心将使矩形均匀延伸到两侧,并仅向下延伸,同时保持顶部边缘位置不变。

水平布局组 (Horizontal Layout Group)

水平布局组组件将其子布局元素并排放置在一起。子布局元素的宽度根据以下规则由各自的最小宽度、偏好宽度和灵活宽度决定:
  • 所有子布局元素的最小宽度相加,并加上它们之间的间距。得到的结果便是水平布局组的最小宽度。
  • 所有子布局元素的偏好宽度相加,并加上它们之间的间距。得到的结果便是水平布局组的偏好宽度。
  • 如果水平布局组处于其最小宽度或更小值,则所有子布局元素也将具有最小宽度。
  • 水平布局组越接近其偏好宽度,每个子布局元素也越接近偏好宽度。
  • 如果水平布局组宽度大于其偏好宽度,则会根据各子布局元素的灵活宽度按比例为子布局元素分配额外的可用空间。

垂直布局组 (Vertical Layout Group)

垂直布局组组件将子布局元素纵向放置。子布局元素的高度根据以下规则由各自的最小高度、偏好高度和灵活高度决定:
  • 所有子布局元素的最小高度相加,并加上它们之间的间距。得到的结果便是垂直布局组的最小高度。
  • 所有子布局元素的偏好高度相加,并加上它们之间的间距。得到的结果便是垂直布局组的偏好高度。
  • 如果垂直布局组处于其最小高度或更小值,则所有子布局元素也将具有最小高度。
  • 垂直布局组越接近其偏好高度,每个子布局元素也越接近偏好高度。
  • 如果垂直布局组高度大于其偏好高度,则会根据各子布局元素的灵活高度按比例为子布局元素分配额外的可用空间。

网格布局组 (Grid Layout Group)

与其他布局组不同,网格布局组会忽略其包含的布局元素的最小大小、偏好大小和灵活大小属性,而是为所有这些元素分配固定大小,此大小则由网格布局组本身的 Cell Size 属性进行定义。
将网格布局组用作自动布局设置的一部分(例如将其与内容大小适配器 (Content Size Fitter) 一起使用)时,需要注意一些特殊注意事项。
自动布局系统会独立计算水平和垂直大小。这一点可能与网格布局组不一致;网格布局组的行数取决于列数或反之。
对于任何给定数量的单元格,行数和列数的不同组合可以使网格适应其内容。为了支持布局系统,可使用 Constraint 属性指定希望该表具有固定列数或行数。
建议按照以下方法将布局系统与内容大小适配器结合使用:
灵活宽度和固定高度
要设置具有灵活宽度和固定高度的网格,从而使网格随着添加的元素增多而水平扩展,可设置如下属性:
  • Grid Layout Group Constraint:Fixed Row Count
  • Content Size Fitter Horizontal Fit:Preferred Size
  • Content Size Fitter Vertical Fit:Preferred Size 或 Unconstrained
如果 Vertical Fit 设置为 Unconstrained,则由您为网格提供足够大的高度来适应指定的单元格行数。
固定宽度和灵活高度
要设置具有固定宽度和灵活高度的网格,从而使网格随着添加的元素增多而垂直扩展,可设置如下属性:
  • Grid Layout Group Constraint:Fixed Column Count
  • ontent Size Fitter Horizontal Fit:Preferred Size 或 Unconstrained
  • Content Size Fitter Vertical Fit:Preferred Size
如果 Horizontal Fit 设置为 Unconstrained,则由您为网格提供足够大的宽度来适应指定的单元格列数。
灵活宽度和灵活高度
如果希望网格同时具有灵活宽度和灵活高度,这是可以的,但无法控制具体的行数和列数。网格将尝试使行数和列数大致相同。可设置如下属性:
  • Grid Layout Group Constraint:Flexible
  • Content Size Fitter Horizontal Fit:Preferred Size
  • Content Size Fitter Vertical Fit:Preferred Size

案例:使 UI 元素适应其内容的大小

通常,在使用矩形变换定位 UI 元素时,应手动指定其位置和大小(可选择性地包括使用父矩形变换进行拉伸的行为)。
但是,有时可能希望自动调整矩形的大小来适应 UI 元素的内容。为此,可添加名为内容大小适配器 (Content Size Fitter) 的组件。

适应文本大小

为了使带有文本组件的矩形变换适应文本内容,请将内容大小适配器组件添加到具有文本组件的同一游戏对象。然后将 Horizontal Fit 和 Vertical Fit 下拉选单设置为 Preferred 设置。
此处的工作原理是文本组件充当布局元素,可提供有关最小大小和偏好大小的大小信息。在手动布局中,不会使用此信息。内容大小适配器是一种布局控制器,可监听布局元素提供的布局信息,并根据此信息控制矩形变换的大小。
当 UI 元素自动调整大小来适应其内容时,应特别注意矩形变换的轴心。当元素调整大小时,轴心将保持在原位,因此通过设置轴心位置,可控制元素扩展或收缩的方向。例如,如果轴心位于中心,则元素将在所有方向上均匀扩展,如果轴心位于左上角,则元素将向右和向下扩展。

适应具有子文本的 UI 元素的大小

如果 UI 元素(比如按钮)具有一个背景图像和一个带有文本组件的子游戏对象,这种情况下可能会希望整个 UI 元素适应文本的大小 - 可能还需要一些填充。
为此,首先将水平布局组 (Horizontal Layout Group) 添加到 UI 元素,然后也添加内容大小适配器。将 Horizontal Fit 和/或 Vertical Fit 设置为 Preferred 设置。此外,可使用水平布局组中的填充属性来添加和调整填充。
为什么使用水平布局组?其实,也可以是垂直布局组 (Vertical Layout Group),只要只有一个子项,两者的结果就是相同的。
水平(或垂直)布局组既可用作布局控制器,也可用作布局元素。首先,布局组会监听该组内的子项(在本示例中为子文本组件)提供的布局信息。然后,布局组将确定该组必须为多大(最小大小和偏好大小)才能够包含所有子项,并充当布局元素来提供这些有关最小大小和偏好大小的信息。
内容大小适配器会监听同一游戏对象上的任何布局元素提供的布局信息 - 在本示例中由水平(或垂直)布局组提供。根据设置,内容大小适配器随后会基于此信息控制矩形变换的大小。
一旦设置了矩形变换的大小,水平(或垂直)布局组将确保根据可用空间来定位其子项并调整大小。请参阅有关水平布局组的页面,详细了解如何控制其子项的位置和大小。

使布局组的子项适应各自的大小

如果有一个布局组(水平或垂直)并希望该组中的每个 UI 元素都适应其各自的内容,该怎么做?
这种情况下不能为每个子项添加内容大小适配器。原因是内容大小适配器想要控制自己的矩形变换,但父布局组也想要控制子矩形变换。因此会产生冲突,结果是不明确的行为。
但是,也没有必要这样做。父布局组已经可以使每个子项适应内容的大小。需要做的是禁用布局组上的 Child Force Expand 开关。如果子项本身也是布局组,可能还需要禁用这些子项上的 Child Force Expand 开关。
一旦子项不再以灵活宽度进行扩展,便可使用 Child Alignment 设置在布局组中指定子项的对齐方式。
如果希望某些子项进行扩展以便填充额外的可用空间,而其他子项不扩展,该怎么办?向需要扩展的子项添加布局元素组件并在这些布局元素上启用 Flexible Width 或 Flexible Height 属性,即可轻松控制此行为。父布局组仍应禁用 Child Force Expand 开关,否则所有子项将灵活扩展。
一个游戏对象可以有多个组件,每个组件都提供有关最小大小、偏好大小和灵活大小的布局信息。优先级系统将确定哪些值优先于其他值。布局元素组件的优先级高于文本、图像和布局组组件,因此可用于覆盖它们提供的任何布局信息值。
布局组监听子项提供的布局信息时,还会考虑覆盖的灵活大小。然后,在控制子项的大小时,布局组不会让子项的大小超过偏好大小。但是,如果布局组启用了 Child Force Expand 选项,则始终会使所有子项的灵活大小至少为 1。
Tags:
尹煜
北京宏宇航天技术有限公司Unity工程师 - Programmer
31
Comments
雨落随风
17 days ago
程序猿
很棒
0
g
ggrmac
22 days ago
写的很详细!
0
阿普唑仑~
22 days ago
谢谢
0