Refs:
https://gwb.tencent.com/community/detail/117690

前言

原本是想继续上一篇的内容梳理 LayoutRebuilder 的源码,但发现布局系统的内容非常多且抽象,如果不结合实际的使用来分析很难理解透彻;且大部分的功能在日常使用 Unity 的过程中出现的频率都非常高,例如 HorizontalLayoutGroupVerticalLayoutGroupGridLayoutGroup 等等。在我们使用 UGUI 的过程中,经常会被这些布局组件的繁杂属性所困扰,因此这里打算先系统性地介绍一下布局系统的使用,之后再继续 LayoutRebuilder 的源码解析。

LayoutSystem 介绍

Auto Layout System 是基于 Rect Transform Layout System 之上的系统(关于 RectTransform 可以参考 Unity3D RectTransform使用详解:布局、属性、方法、设计),自动调整一个或多个的元素大小、位置、间格,又分为 Layout Controllers(父物件) 与 Layout Elements(子物件) 两部分,一个简单的 Auto Layout 架构如下

image.png

Layout Element (子物件)

一个 Layout Element 是一个可以被 Layout Controller 控制的物件,它可以有以下属性:

image.png

  • Min Width/Height: 最小宽度/高度
  • Preferred Width/Height: 首选宽度/高度
  • Flexible Width/Height: 弹性宽度/高度

点选UI后,可以在 Inspector 最下方切换为 Layout Properties 看到资讯

image.png

Layout Controllers 透过不同的布局方式,取得 Layout Element size 分配子物件,基本原则如下

  1. 首先分配 Minimum Size

  2. 如果还有足够空间,分配 Preferred Size

  3. 如果还有额外空间,分配 Flexible Size

从以下图片可以看出图片宽度的增长方式:

image.png

首先分配 Minimum Size (300,红色部分);如果还有足够空间,分配 Preferred Size (300~500,绿色部分);如果还有额外空间,分配 Flexible Size:1 (500~700,蓝色部分)

比较特别的是 Flexible,他是代表著整个大小的比例,如果 Layout 下有2个物体,分别给 Flexible 设定为 0.3 与 0.7,那比例就会变成下图 (3:7)

image.png

另外要注意的是,Text、Image Component 会根据内容大小自动分配 Preferred Size。

Layout Controller (父物件)

Layout Group

不会控制 Layout Controllers (父物件)自身大小,而是控制子物件大小与位置,在大多数情况下,根据每个元素的 minimum、preferred、flexible 大小分配适当的空间,layout group 之间也可以嵌套,又分为 Horizontal(水平)、Vertical(垂直)、Grid(格状) 3种。

Horizontal Layout Group

Horizontal Layout Group 组件的可配置属性如下:

image.png

  • Padding:填充内部空间

  • Spacing:每个元素间格

  • Child Alignment:当没有填满全部空间时,子物件对齐位置

  • Child Force Expand:强制控制子物件填满空间

以一个例子来说明各参数的含义,这里我们为父物体添加一个 Horizontal Layout Group 组件,然后在其下添加多个 button,完成后如下,当大小改变时会自动分配子物件大小。

  1. Layout Group 的 Padding 与 Spacing

    test.gif

    此时在 Button 的 Rect Transform Component 就不能进行调整,因为我们已经透过 Horizontal Layout Group 进行分配空间,在 Rect Transform 会显示目前被哪个 Layout Group 控制

    image.png

    将 Padding 数值调整如图,可以看出填充区域

    image.png

    将 Spacing 数值调整如图,可以看出元素区间

    image.png

  2. Layout Element 的 Min Width

    接下来我们将5个 Button 增加 Layout Element Component 覆盖预设大小,用于手动设定每个元素的大小。此时将 Horizontal Layout Group 的 Child Force Expand Width 取消勾选,不强制子物件填满额外空间,而是透过 Layout Element 手动设定。这里使用几种不同的设定,来理解 Horizontal Layout Group 是怎么取得 Layout Element size 分配子物件。

    将 5个 Button 的 Layout Element Min Width 分别改为 20、30、40、50、60,此时可以看出每个 Button 宽度分布,改变父物件大小时子物件大小并不会改变,因为只有分配 Min Width,并不会分配额外有效空间。

    test.gif

    此时改变 Horizontal Layout Group 的 Child Alignment,可以看出元素对齐

    test.gif

    父物件 Layout Properties Min Width = 5个按钮宽(20 30 40 50 60=200) Spacing(40) Padding Left、Right(20) = 260

    image.png

  3. Layout Element 的 Preferred Width

    现在将第1个 Button 的 Layout Element 数值调整如图

    image.png

    父组件首先分配 Minimum Size(20),空间足够的情况下,将会分配剩下的 Preferred Size (20~100 空间),如下所示。

    test.gif

  4. Layout Element 的 Flexible Width

    现在将第1个 Button 的 Layout Element 数值调整如图

    image.png

    父组件首先分配 Minimum Size(20),空间足够的情况下,将会分配剩下的 Preferred Size (20~100 空间),如果还有额外空间,将会分配 Flexible Size,如下所示。

    test.gif

  5. Layout Group 的 Child Force Expand

    现在将 Horizontal Layout Group 的 Child Force Expand Width 勾选,让子物件强制填满。

    父组件首先分配 Minimum Size(20),空间足够的情况下,将会分配剩下的 Preferred Size (20~100 空间),如果还有额外空间,将会分配 Flexible Size 与 Child Force Expand Width,如下所示。

    test.gif

小结:

上面我们看到,所有元素会先被分配 Minimum Size,接下来还有足够空间,将会分配剩下的 Preferred Size,最后才是 Flexible Size 与 Child Force Expand Width。至此我们了解到 Horizontal Layout Group 是如何为子物件分配空间的,Vertical Layout Group 与 Grid Layout Group 也是类似的,只是方向与分配方式不同,这里就不再赘述。

Grid Layout Group

Grid Layout Group 是一个网格布局组件,可以让子物件按照网格排列,其可配置属性如下:

image.png

  • Padding:填充内部空间

  • Cell Size:每个元素大小

test.gif

  • Spacing:每个元素的间隔

  • Start Corner:起始角落,分为左上、右上、左下、右下,决定元素排列的起始位置。注意观察元素的数字

test.gif

  • Start Axis:起始轴,分为水平、垂直,决定元素排列的方向。注意观察元素的数字

test.gif

  • Child Alignment:当没有填满全部空间时,子物件对齐位置

  • Constraint:约束,分为 Flexible、Fixed Row Count、Fixed Column Count 3种,决定元素排列的方式

    1. Flexible:自由排列,根据 Cell Size 与 Spacing 自由排列元素

    2. Fixed Row Count:固定行数,根据 Row Count 与 Cell Size 与 Spacing 排列元素

    3. Fixed Column Count:固定列数,根据 Column Count 与 Cell Size 与 Spacing 排列元素

Content Size Fitter

上面我们介绍了 Layout Group,该组件会根据父物体的大小,自动调整子物体的大小与位置,但是有时候我们需要根据子物体的大小,自动调整父物体的大小,这时候就需要 Content Size Fitter 组件。

Content Size Fitter 组件的可配置属性如下:

image.png

  • Horizontal Fit:水平适配,分为 Unconstrained、Min Size、Preferred Size、Max Size 4种,决定水平方向的适配方式

  • Vertical Fit:垂直适配,分为 Unconstrained、Min Size、Preferred Size、Max Size 4种,决定垂直方向的适配方式

其中,Unconstrained 代表不适配,Min Size 代表以子物体最小大小适配,Preferred Size 代表以子物体首选大小适配。

下面我们以一个例子来说明 Content Size Fitter 的使用:

首先我们在 Canvas 下创建一个父物体,然后添加 Horizontal Layout Group 组件,这时如果在父物体下添加 Button,可以看到在 Horizontal Layout Group 的控制下,子物体会自动排列并调整大小,而父物体本身的大小并不会改变:

test.gif

我们为 Button 们添加 Layout Element 组件,然后调整 Min Width 以覆盖预设大小。接下来我们为父物体添加 Content Size Fitter 组件,将 Horizontal Fit 调整为 Min Size。

image.png

可以看到父物体的大小会根据子物体的最小大小自动调整:

test.gif

这样的设置就可以满足父物体根据子物体大小自动调整的需求。

此外,通过调整父物体的 Pivot,还可以控制父物体的缩放方向,如下图所示:

test.gif

Aspect Ratio Fitter

Aspect Ratio Fitter 组件可以根据子物体的宽高比例,调整父物体的大小,其可配置属性如下:

image.png

  • Aspect Mode:
  1. None:不调整

  2. Width Controls Height:宽度控制高度

    以 Width 为基准,依据 Aspect Ratio 调整 Height

    test.gif

    当 Width 改变时,Height 会依比例改变

    test.gif

  3. Height Controls Width:同理,以 Height 为基准,依据 Aspect Ratio 调整 Width

  4. Fit In Parent:适应父物体

    依据比例将 宽高、位置、anchors 自动调整,使此图形大小在父物件中完全贴齐,此模式可能不会包覆所有空间。

    当子物体的比例与父物体相同时,会完全贴齐;当子物体的比例与父物体不同时,会按比例缩小子物体。

    test.gif

    调整父物件大小,物体会依据比例贴齐父物件

    test.gif

  5. Envelope Parent:包裹父物体

    依据比例将 宽高、位置、anchors自动调整,使此图形大小完全包覆父物件,此模式可能会超出空间。

    当子物体的比例与父物体相同时,会完全包裹;当子物体的比例与父物体不同时,会按比例放大子物体。

    test.gif

    调整父物件大小,物体会依据比例包裹父物件

    test.gif

总结

至此我们就分析完了 Auto Layout System 的基本使用,通过 Layout Group、Content Size Fitter、Aspect Ratio Fitter 等组件,我们可以很方便地实现 UI 的自动布局,这些组件在我们日常使用 Unity 进行 UI 开发时会经常用到,掌握这些组件的使用,可以让我们更高效地进行 UI 开发。

在下一篇文章中,我们将继续分析 LayoutRebuilder 的源码,了解其是如何实现 UI 的自动布局的。