Rect Transform layout系统足够灵活,可以处理许多不同类型的布局,还允许以完全自由的方式放置元素。然而,有时需要一些更结构化的东西。
自动布局系统提供了将元素放置在嵌套布局组(如水平组、垂直组或网格)中的方法。它还允许根据包含的内容自动调整元素的大小。例如,可以动态调整按钮的大小,使其完全适合其文本内容和一些填充。
自动布局系统是在矩形变换布局系统的基础上建立起来的系统。它可以选择性地用于某些或所有元素。
一、理解布局元素(Layout Element)
自动布局系统是基于布局元素(layout elements)和布局控制器(layout controllers)的概念。布局元素是具有Rect Transform 和其他可选组件的游戏对象。布局元素对它应该具有的大小有一定的了解。布局元素不会直接设置自己的大小,但是作为布局控制器的其他组件可以使用它们提供的信息来计算要使用的大小。
布局元素(layout element)具有定义其自身属性的属性:
- Minimum width:最小宽度
- Minimum height:最小的高度
- Preferred width:首选的宽度
- Preferred height:首选的高度
- Flexible width:灵活的宽度
- Flexible height:灵活的高度
使用布局元素提供的信息的布局控制器组件的例子有Content Size Fitter和各种Layout Group组件。
布局组中布局元素的大小的基本原则如下:
- 首先分配最小大小。
- 如果有足够的可用空间,则分配首选大小。
- 如果有额外的可用空间,则分配灵活的大小。
任何具有Rect Transform的游戏对象都可以作为布局元素。默认情况下,它们的最小、首选和灵活大小为0。某些组件在添加到游戏对象时,将更改这些布局属性。
Image和Text组件是提供布局元素属性的组件的两个示例。它们更改首选的(preferred)宽度和高度以匹配精灵或文本内容。
Layout Element Component
如果您想要重写最小的、首选的或灵活的大小,您可以通过向游戏对象添加LayoutElement来实现。
imageLayoutElement允许您覆盖一个或多个布局属性的值。为要覆盖的属性启用复选框,然后指定要覆盖的值。
Minimum尺寸和Preferred尺寸在常规单位中定义,而Flexible尺寸在相对单位中定义。
如果任何布局元素的Flexible大小都大于零,这意味着所有可用空间都将被填满。兄弟姐妹的相对Flexible大小值决定了每个兄弟姐妹填充的可用空间的比例。通常,Flexible宽度和高度设置为0或1。
在某些情况下,指定Preferred大小和Flexible大小都是有意义的。只有在所有Preferred的大小被完全分配之后,才会分配Flexible的大小。
因此,指定了Flexible大小但没有Preferred大小的布局元素将保持其最小大小,直到其他布局元素增长到它们的全部Preferred大小,然后才开始基于额外的可用空间增长。通过指定一个Flexible的大小,可以避免这种情况,元素可以与具有Preferred大小的其他布局元素一起增长到其Preferred大小,然后在分配了所有Flexible大小后进一步增长。
二、理解布局控制器(Layout Controllers)
布局控制器是控制一个或多个布局元素(也就是带有RectTransform的游戏对象)的大小和可能的位置的组件。布局控制器可以控制它自己的布局元素(与它在自己上的游戏对象相同),也可以控制子布局元素。
作为布局控制器的组件本身也可以同时作为布局元素。
1. Content Size Fitter内容大小适配器
Content Size Fitter功能就像一个布局控制器,控制自己布局元素的大小。大小由游戏对象上布局元素组件提供的最小或首选大小决定。这种布局元素可以是图像或文本组件、布局组或布局元素组件。
查看自动布局系统运行的最简单方法是向带有文本组件的游戏对象中添加Content Size Fitter 组件。
image属性:
- Unconstrained:不要基于布局元素驱动宽度。
- Min Size:根据布局元素的最小宽度驱动宽度。
- Preferred Size:根据布局元素的Preferred宽度驱动宽度。
如果您将Horizontal Fit或Vertical Fit设置为Preferred,RectTransform将调整其宽度和/或高度以适合文本内容。
值得记住的是,当RectTransform被调整大小时——无论是通过Content Size Fitter还是其他什么——调整大小都是围绕pivot进行的。这意味着调整的方向可以用pivot来控制。
例如,当pivot位于中心时,Content Size Fitter将向各个方向均匀地展开Rect Transform。当pivot位于左上角时,Content Size Fitter将把Rect Transform向下展开并向右展开。
2. Aspect Ratio Fitter长宽比适配器
Aspect Ratio Fitter功能就像一个布局控制器,控制自己布局元素的大小。
image它可以调整高度以适应宽度,反之亦然,或者它可以使元素适合于它的父元素或封装它的父元素。The Aspect Ratio Fitter不考虑布局信息,如最小尺寸和首选尺寸。
属性:
-
Aspect Mode:是如何调整矩形大小以加强长宽比的。
-
None:不要使矩形与长宽比相匹配。
-
Width Controls Height:高度根据宽度自动调整。
-** Height Controls Width**:宽度会根据高度自动调整。
-
Fit In Parent:宽度,高度,位置,和锚是自动调整,使矩形适应于父母的矩形,同时保持长宽比。(保持纵横比的缩放该UI,使其上下或者左右与父物体对其且在父物体内部)
- Envelope Parent:宽度、高度、位置和锚点会自动调整,使矩形覆盖整个父节点的区域,同时保持长宽比。此矩形可以比父矩形扩展得更远。(跟上类似,只是上是限制在父物体内部,而这个却超出父物体,覆盖整个父物体)
- Aspect Ratio:执行长宽比。这是宽度除以高度。
3/ Layout Groups###
布局组充当布局控制器,控制子布局元素的大小和位置。例如,水平布局组将其子元素放在一起,网格布局组将其子元素放在网格中。
布局组不控制自己的大小。相反,它的功能是布局元素本身,可以由其他布局控制器控制或手动设置。
无论布局组的大小是多少,在大多数情况下,它都会根据所报告的最小、首选和灵活的大小为每个子布局元素分配适当的空间。布局组也可以这样任意嵌套。
Horizontal/Vertical Layout Group###
image属性:
- Padding:布局组边缘内的填充(留空作偏移)。
- Spacing:布局元素之间的间距。
- Child Alignment:如果子布局元素没有填满所有可用空间,则使用的对齐方式。
- Child Controls Size:布局组是否控制其子元素的宽度和高度。
- Child Force Expand:是否强迫孩子们扩大空间以填补额外的可用空间。
水平布局组组件将其子布局元素并排放置在一起。它们的宽度由它们各自的最小宽度、首选宽度和灵活宽度根据以下模型确定:
将所有子布局元素的最小宽度添加到一起,并添加它们之间的间距。结果是水平布局组的mimimum宽度。
所有子布局元素的首选宽度被添加到一起,它们之间的间距也被添加。结果是水平布局组的首选宽度。
如果水平布局组的宽度小于或等于最小值,则所有子布局元素的宽度也都是最小值。
水平布局组越接近其首选宽度,每个子布局元素也就越接近其首选宽度。
如果水平布局组比其首选宽度更宽,则它将根据其各自的灵活宽度按比例将额外可用空间分配给子布局元素。
Grid Layout Group
image属性:
- Padding:布局组边缘内的填充(留空作偏移)。
- Cell Size:用于组中每个布局元素的大小。
- Spacing:布局元素之间的间距。
- Child Alignment:如果子布局元素没有填满所有可用空间,则使用的对齐方式。
- Child Controls Size:布局组是否控制其子元素的宽度和高度。
- Child Force Expand:是否强迫孩子们扩大空间以填补额外的可用空间。
- Start Corner:第一个元素所在的角落。
- Start Axis:放置元素的主轴。在新行开始之前,Horizonal将填充整个行。在开始新列之前,Vertical将填充整个列。
- Child Alignment:如果布局元素没有填满所有可用空间,则对其进行对齐。
- Constraint:将网格限制为固定数量的行或列,以帮助自动布局系统。
与其他布局组不同,网格布局组忽略所包含布局元素的最小、首选和灵活大小属性,而是为所有这些元素分配一个固定的大小,这个大小由网格布局组本身的单元格大小属性定义。
在使用网格布局组作为自动布局设置的一部分时,需要注意一些特殊的注意事项,例如使用它与 Content Size Fitter一起使用。
自动布局系统独立计算水平和垂直尺寸。这可能与网格布局组(行数取决于列数,反之亦然)不一致。
对于任意数量的单元格,行计数和列计数的不同组合可以使网格适合其内容。为了帮助布局系统,可以使用Constraint属性指定表的列或行数是固定的。
以下是使用布局系统与Content Size Fitter的一些建议:
1)宽度灵活,高度固定
要设置一个具有灵活宽度和固定高度的网格,其中随着添加更多元素,网格水平扩展,您可以将这些属性设置为:
- Grid Layout Group Constraint:Fixed Row Count固定行数
- Content Size Fitter Horizontal Fit:Preferred Size
- Content Size Fitter Vertical Fit: Preferred Size or Unconstrained
如果使用unconstrained Vertical Fit,则由您决定网格的高度是否足够大,以适合指定的单元格行数。
2)固定的宽度和灵活的高度
要设置一个固定宽度和灵活高度的网格,其中随着添加更多元素,网格垂直展开,您可以将这些属性设置为:
- Grid Layout Group Constraint: Fixed Column Count
- Content Size Fitter Horizontal Fit: Preferred Size or Unconstrained
- Content Size Fitter Vertical Fit: Preferred Size
如果使用unconstrained Horizonal Fit,则由您决定网格的宽度是否足够大,以适合指定的单元格列计数。
3)灵活的宽度和高度
如果您想要一个同时具有灵活宽度和高度的网格,您可以这样做,但是您无法控制特定的行数和列数。网格将尝试使行和列的计数近似相同。您可以按照以下方式设置这些属性:
- Grid Layout Group Constraint: Flexible
- Content Size Fitter Horizontal Fit: Preferred Size
- Content Size Fitter Vertical Fit: Preferred Size
驱动Rect Transform属性
由于自动布局系统中的布局控制器可以自动控制某些UI元素的大小和位置,所以这些大小和位置不应该同时通过检查器或场景视图手动编辑。无论如何,这些更改后的值将由布局控制器在下一次布局计算中重置。
Rect Transform有一个driven properties(驱动属性)的概念来解决这个问题。例如,将Horizontal Fit属性设置为Minimum或Preferred的Content Size Fitter将驱动同一游戏对象上的Rect Transform的宽度。宽度将显示为只读,并且Rect Transform顶部的一个小信息框将通知一个或多个属性由Conten Size Fitter驱动。
image除了防止手工编辑之外,受驱动的Rect Transform属性还有其他原因。布局可以通过改变游戏视图的分辨率或大小来改变。这反过来又会改变布局元素的大小或位置,从而改变驱动属性的值。但如果仅仅因为游戏视图被调整了大小,场景就被标记为有未保存的更改,这是不可取的。为了防止这种情况发生,驱动属性的值不会作为场景的一部分保存,对它们的更改不会将场景标记为已更改。
技术细节
自动布局系统内置了某些组件,但也可以创建新的组件,以定制的方式控制布局。这是通过让组件实现由自动布局系统识别的特定接口来实现的。
界面布局
如果组件实现了接口ILayoutElement,则自动布局系统将其视为布局元素。
如果组件实现了接口ILayoutGroup,那么它将驱动其子组件的Rect Transform。
如果组件实现了接口ILayoutSelfController,那么它应该驱动自己的RectTransform。
布局计算
自动布局系统按照以下顺序对布局进行评估和执行:
通过在ILayoutElement组件上调用CalculateLayoutInputHorizontal()、CalculateLayoutInputVertical()来计算布局元素的最小、优先和灵活宽度。这是按照自底向上的顺序执行的,在此顺序中,子女在其父母之前计算,以便父母可以在自己的计算中考虑子女的信息。
通过在ILayoutController组件上调用SetLayoutHorizontal()、SetLayoutVertical()来计算和设置布局元素的有效宽度。这是按照自顶向下的顺序执行的,在这种顺序中,子宽度的分配需要基于父级可用的全宽度。在这一步之后,布局元素的RectTransform有了新的宽度。(ILayoutGroup上层也是ILayoutController)
从上图可以看出,自动布局系统首先对宽度进行评估,然后再对高度进行评估。因此,计算的高度可能取决于宽度,但计算的宽度永远不可能取决于高度。
触发布局重建
当组件上的属性发生变化,导致当前布局不再有效时,需要重新计算布局。这可以通过调用:
LayoutRebuilder.MarkLayoutForRebuild(转换为RectTransform);
重建不会立即发生,但是在当前帧的末尾,在呈现发生之前。它不是即时的原因是,这可能会导致在同一帧中多次重新构建布局,这将不利于性能。
何时应该触发重建的指导方针:
- 在可更改布局的属性的setter中。
- 在这些回调函数:
- OnEnable
- OnDisable
- OnRectTransformDimensionsChange
- OnValidate(只在编辑器中需要,在运行时不需要)
- OnDidApplyAnimationProperties
网友评论