UGUI-自動(dòng)布局
矩形變換布局系統(tǒng)足夠靈活,可以處理許多不同類型的布局,還允許以完全自由形式的方式布置元素。但是,有時(shí)可能需要更結(jié)構(gòu)化的系統(tǒng)。
自動(dòng)布局系統(tǒng)提供了將元素放置在嵌套布局組(如水平組、垂直組或網(wǎng)格)中的方法。使用該系統(tǒng)還可以根據(jù)元素包含的內(nèi)容自動(dòng)調(diào)整元素的大小。例如,按鈕可動(dòng)態(tài)調(diào)整大小來適合其文本內(nèi)容并加上一些填充。
自動(dòng)布局系統(tǒng)是建立在基本矩形變換布局系統(tǒng)之上的系統(tǒng)。您可以選擇將其用于某些或所有元素。
了解布局元素
自動(dòng)布局系統(tǒng)基于布局元素和布局控制器的概念。一個(gè)布局元素是具有矩形變換以及其他可選組件的游戲?qū)ο?。布局元素?duì)自身應(yīng)具有的大小有一定的了解。布局元素不直接設(shè)置自身的大小,而是可由用作布局控制器的其他組件使用布局元素提供的信息來計(jì)算布局元素要使用的大小。
布局元素具有相應(yīng)的屬性來定義自身的以下參數(shù):
Minimum width
Minimum height
Preferred width
Preferred height
Flexible width
Flexible height
使用布局元素提供的信息的布局控制器組件示例為內(nèi)容大小適配器 (Content Size Fitter)?以及各種布局組 (Layout Group)?組件。布局組中的布局元素大小設(shè)置的基本原則如下:
首先分配最小大小。
如果有足夠的可用空間,則分配偏好大小。
如果有額外的可用空間,則分配靈活大小。
任何帶有矩形變換的游戲?qū)ο蠖伎梢宰鳛椴季衷?。默認(rèn)情況下,布局元素的最小大小、偏好大小和靈活大小均為 0。某些組件在添加到游戲?qū)ο髸r(shí)將更改這些布局屬性。
圖像 (Image) 和文本 (Text) 組件是提供布局元素屬性的兩個(gè)組件示例。這些組件會(huì)更改偏好寬度和高度來匹配精靈或文本內(nèi)容。
布局元素組件
如果要覆蓋最小大小、偏好大小或靈活大小,可通過向游戲?qū)ο筇砑硬季衷亟M件來實(shí)現(xiàn)。

使用布局元素組件可以覆蓋一個(gè)或多個(gè)布局屬性的值。啟用要覆蓋的屬性的復(fù)選框,然后指定要用于覆蓋的值。
請(qǐng)參閱布局元素參考頁(yè)面以了解更多信息。
了解布局控制器
布局控制器是控制一個(gè)或多個(gè)布局元素(即具有矩形變換的游戲?qū)ο螅┑拇笮『涂赡芪恢玫慕M件。布局控制器可以控制自己的布局元素(其本身所在的同一游戲?qū)ο螅?,或者也可控?strong>子布局元素。
用作布局控制器的組件本身也可以同時(shí)用作布局元素。
內(nèi)容大小適配器 (Content Size Fitter)
內(nèi)容大小適配器充當(dāng)布局控制器,可用于控制其自身布局元素的大小。查看實(shí)際自動(dòng)布局系統(tǒng)的最簡(jiǎn)單方法是向帶有文本組件的游戲?qū)ο筇砑觾?nèi)容大小適配器組件。

如果將 Horizontal Fit 或 Vertical Fit 設(shè)置為 Preferred,則矩形變換將調(diào)整其寬度和/或高度以適應(yīng)文本內(nèi)容。
請(qǐng)參閱內(nèi)容大小適配器參考頁(yè)面以了解更多信息。
寬高比適配器 (Aspect Ratio Fitter)
寬高比適配器充當(dāng)布局控制器,可用于控制其自身布局元素的大小。

寬高比適配器可以調(diào)整高度來適應(yīng)寬度或反之,也可以使元素在其父項(xiàng)內(nèi)部適應(yīng)或包裹其父項(xiàng)。寬高比適配器不考慮布局信息,例如最小大小和偏好大小。
請(qǐng)參閱寬高比適配器參考頁(yè)面以了解更多信息。
布局組
布局組充當(dāng)布局控制器,可用于控制其子布局元素的大小和位置。例如,水平布局組將其子項(xiàng)并排放置,而網(wǎng)格布局組將其子項(xiàng)放在網(wǎng)格中。
布局組不控制自身的大小。相反,布局組自身作為布局元素,可由其他布局控制器進(jìn)行控制或手動(dòng)設(shè)置。
無論分配給布局組的大小如何,在大多數(shù)情況下,布局組都將嘗試根據(jù)其子布局元素報(bào)告的最小大小、偏好大小和靈活大小為每個(gè)子布局元素分配適當(dāng)?shù)目臻g量。布局組也可通過這種方式任意嵌套。
請(qǐng)參閱水平布局組、垂直布局組和網(wǎng)格布局組的參考頁(yè)面以了解更多信息。
矩形變換驅(qū)動(dòng)屬性
由于自動(dòng)布局系統(tǒng)中的布局控制器可以自動(dòng)控制某些 UI 元素的大小和位置,因此不應(yīng)通過 Inspector 或 Scene 視圖同時(shí)手動(dòng)編輯這些大小和位置。無論如何,即使更改這些值,布局控制器在下一次布局計(jì)算時(shí)也會(huì)重置這些值。
矩形變換有一個(gè)驅(qū)動(dòng)屬性的概念可解決這一問題。例如,Horizontal Fit 屬性設(shè)置為 Minimum 或 Preferred 的內(nèi)容大小適配器將在同一個(gè)游戲?qū)ο笊向?qū)動(dòng)矩形變換的寬度。該寬度將顯示為只讀狀態(tài),并且矩形變換頂部的小信息框?qū)⒅甘疽粋€(gè)或多個(gè)屬性由內(nèi)容大小適配器驅(qū)動(dòng)。
除了防止手動(dòng)編輯之外,矩形變換使用驅(qū)動(dòng)屬性還有其他原因。只需更改 Game 視圖的分辨率或大小即可更改布局。這樣一來可以改變布局元素的大小或位置,進(jìn)而改變驅(qū)動(dòng)屬性的值。但是,通常不希望僅僅因?yàn)檎{(diào)整了 Game 視圖的大小就將場(chǎng)景標(biāo)記為具有未保存的更改。為防止這種情況,驅(qū)動(dòng)屬性的值不會(huì)保存為場(chǎng)景的一部分,對(duì)這些屬性的更改也不會(huì)將場(chǎng)景標(biāo)記為已更改狀態(tài)。
技術(shù)細(xì)節(jié)
自動(dòng)布局系統(tǒng)內(nèi)置了一些組件,但也可以創(chuàng)建以自定義方式控制布局的新組件。為此,應(yīng)讓組件實(shí)現(xiàn)可由自動(dòng)布局系統(tǒng)識(shí)別的特定接口。
布局接口
如果組件實(shí)現(xiàn)?ILayoutElement?接口,則自動(dòng)布局系統(tǒng)會(huì)將該組件視為布局元素。
如果組件實(shí)現(xiàn)?ILayoutGroup?接口,則該組件應(yīng)該驅(qū)動(dòng)其子項(xiàng)的矩形變換。
如果組件實(shí)現(xiàn)?ILayoutSelfController?接口,則該組件應(yīng)該驅(qū)動(dòng)自己的 RectTransform。
布局計(jì)算
自動(dòng)布局系統(tǒng)按以下順序計(jì)算和執(zhí)行布局:
1.通過在 ILayoutElement 組件上調(diào)用 CalculateLayoutInputHorizontal 來計(jì)算布局元素的最小寬度、偏好寬度和靈活寬度。此過程以自下而上的順序執(zhí)行,即子項(xiàng)的計(jì)算先于父項(xiàng),這樣父項(xiàng)可以在自己的計(jì)算中參考子項(xiàng)的信息。 2.通過在 ILayoutController 組件上調(diào)用 SetLayoutHorizontal 來計(jì)算和設(shè)置布局元素的有效寬度。此過程自上而下的順序執(zhí)行,即子項(xiàng)的計(jì)算晚于父項(xiàng),因?yàn)樽禹?xiàng)寬度的分配需要基于父項(xiàng)中可用的完整寬度。在此步驟之后,布局元素的矩形變換便有了新的寬度。 3.通過在 ILayoutElement 組件上調(diào)用 CalculateLayoutInputVertical 來計(jì)算布局元素的最小高度、偏好高度和靈活高度。此過程以自下而上的順序執(zhí)行,即子項(xiàng)的計(jì)算先于父項(xiàng),這樣父項(xiàng)可以在自己的計(jì)算中參考子項(xiàng)的信息。 4.通過在 ILayoutController 組件上調(diào)用 SetLayoutVertical 來計(jì)算和設(shè)置布局元素的有效高度。此過程自上而下的順序執(zhí)行,即子項(xiàng)的計(jì)算晚于父項(xiàng),因?yàn)樽禹?xiàng)高度的分配需要基于父項(xiàng)中可用的完整高度。在此步驟之后,布局元素的矩形變換便有了新的高度。
從上面可以看出,自動(dòng)布局系統(tǒng)首先計(jì)算寬度,然后計(jì)算高度。因此,計(jì)算的高度可取決于寬度,但計(jì)算的寬度決不能取決于高度。
觸發(fā)布局重新構(gòu)建
如果組件上的屬性發(fā)生變化,并可能導(dǎo)致當(dāng)前布局不再有效,則需要重新計(jì)算布局??墒褂靡韵抡{(diào)用來觸發(fā)重新計(jì)算:
LayoutRebuilder.MarkLayoutForRebuild (transform as RectTransform);
重新構(gòu)建不會(huì)立即發(fā)生,而是在當(dāng)前幀結(jié)束時(shí)并在渲染之前進(jìn)行。不立即執(zhí)行的原因是這樣做會(huì)導(dǎo)致布局在同一幀期間可能多次重新構(gòu)建,而這對(duì)性能不利。
應(yīng)按以下準(zhǔn)則觸發(fā)重新構(gòu)建:
在可更改布局的屬性的 setter 中進(jìn)行。
在以下回調(diào)中進(jìn)行:
OnEnable
OnDisable
OnRectTransformDimensionsChange
OnValidate(僅在 Editor 中需要,在運(yùn)行時(shí)不需要)
OnDidApplyAnimationProperties