Unity UGUI布局组件(LayoutGroup、ContentSizeFitter、LayoutElement)详解
UGUI 自动布局是一套基于接口、分两轮四阶段、脏标记延迟批量执行的 C# 排版系统,和 Transform 原生锚点完全隔离; 所有布局组件都围绕两个接口:
- ILayoutElement:提供尺寸信息(min/preferred/flexible),回答 “我应该多大”
- ILayoutController(ILayoutGroup):收集子元素尺寸、分配空间、写 RectTransform,负责 “把大家摆到哪”Unity
三个组件分工:
- LayoutElement:数据提供者,覆盖 / 声明自己的布局尺寸参数(优先级最高)
- LayoutGroup(Vertical/Horizontal/Grid):既是 ILayoutElement(对外输出自己整体尺寸),又是 ILayoutController(控制子物体位置大小)
- ContentSizeFitter:只做 ILayoutController,只读同物体上 ILayoutElement 的尺寸,修改自身 RectTransform 大小,不控制子物体
整套布局不生成 Mesh、不渲染;最终输出只有对 RectTransform 的sizeDelta/anchoredPosition的赋值。
LayoutElement
LayoutElement 是 ILayoutElement 的实现,只负责向外输出尺寸信息,不摆放物体、不修改 RectTransform 位置;用来覆盖 / 重写 Text、Image、LayoutGroup 自带的 min/preferred/flexible 尺寸。
面板逐项解释
1. Ignore Layout
勾选之后:布局系统直接跳过这个物体,不参与父 LayoutGroup 的排版计算。
这个物体还留在层级里,渲染正常
2. Min Width / Min Height(最小宽高)
勾选激活,填写像素值
这个元素被压缩时,不能小于这个尺寸,是底线。
3. Preferred Width / Preferred Height(期望 / 偏好宽高)
勾选激活,填写像素值
元素最理想、刚刚好的尺寸。 空间足够、满足所有 Min 之后,布局系统会尽量把元素给到 Preferred 尺寸。
4. Flexible Width / Flexible Height(弹性宽高)
勾选激活,填写比例系数(不是像素!)
当父 LayoutGroup 分配完所有 Min+Preferred 之后,还有剩余空白空间,就按照所有子 Flexible 的比值瓜分剩下空间。
例子: 水平布局里两个子:A Flexible Width=1,B Flexible Width=2
剩余空间一共 300px → A 拿 100,B 拿 200。
只要任意一个子 Flexible>0,剩余空间就会被瓜分;全部 0,就不会拉伸填充。
区分:Min、Preferred 是像素单位;Flexible 是相对权重。
5. Layout Priority(布局优先级,默认 1)
同一个 GameObject 身上,可能有多个 ILayoutElement(比如 Text + LayoutElement)
- 数值越大优先级越高;布局系统优先读取高优先级组件输出的尺寸,覆盖低优先级;
- Text、Image 原生的 LayoutPriority 默认是 0;LayoutElement 默认是 1。
这就是为什么挂上 LayoutElement 勾选 Preferred,会直接覆盖 Text 文字原生自适应尺寸!LayoutElement 优先级更高。
- 如果优先级相同,布局会取各个组件里更大的尺寸值。
注意理解!!!
LayoutElement 这个组件本身,挂在物体上不需要 “必须有父物体”;但是!它的功能想要生效,就必须有父级的布局控制器(ILayoutController)
布局控制器 = HorizontalLayoutGroup / VerticalLayoutGroup / ContentSizeFitterLayoutElement 的角色:对外上报尺寸数据(min/preferred/flexible)
布局控制器的角色:读取子物体上报的这些尺寸数据,然后去排版、改子物体 RectTransform
1. 单纯挂上 LayoutElement(没有父 Layout 控制器)
你可以随便挂,不会报错,组件面板正常显示
完全没有任何视觉效果,等于白挂
原因:LayoutElement 只是一个数据记录本 。它只会存:我的最小宽 30、期望宽 100、弹性权重 1……但是没有任何东西来读这份记录,就不会有人根据这个数据修改物体大小、位置。
2. 有父布局控制器的时候(这才是 LayoutElement 的正常用法)
LayoutElement 是给【直接父 LayoutGroup】读的不是随便哪个祖先,是直属父物体!
3. 一个特殊情况:同物体身上搭配 ContentSizeFitter
LayoutElement 和 ContentSizeFitter 在同一个物体上,不需要别的父 Layout!
这个场景下,不需要外面再套一个父 LayoutGroup!
作用:用 LayoutElement 强行指定这个物体的 preferred 尺寸,ContentSizeFitter 把自己缩放到这个 preferred 大小。
4. 再单独说 Ignore Layout(LayoutElement 里这个选项特殊)
Ignore Layout 生效条件:
这个物体,必须是某个 LayoutGroup 的直接子物体
父 LayoutGroup 遍历直接子的时候,看到子勾选 Ignore Layout → 跳过它,不参与排版。
如果它根本不是任何 LayoutGroup 的子,Ignore Layout 这个勾,勾了也没用。
Flexible 填 1 不等于占 100%,是比值,必须多个兄弟一起对比;
LayoutPriority 是同一个物体多个 ILayoutElement 之间的优先级,不是兄弟物体之间的优先级
三个 LayoutGroup
Grid Layout Group/Horizontal Layout Group/Vertical Layout Group都是ILayoutController(布局控制器),作用:自动排列自己的直接子物体。
- Horizontal(水平):一排横向摆放,会读取子 LayoutElement 的 Min/Preferred/Flexible(弹性生效)
- Vertical(垂直):一列纵向摆放,会读取子 LayoutElement 的 Min/Preferred/Flexible(弹性生效)
- Grid(网格):格子布局,固定 CellSize,无视子的 Min/Preferred/Flexible(LayoutElement 尺寸除 Ignore Layout 外全部无效)
Horizontal Layout Group 水平布局组(横向一排排列子物体)
Padding内边距。
Spacing子物体和子物体之间的间距(横向间隔)。
Child Alignment在父容器里怎么对齐
Reverse Arrangement反向排列
Control Child Size控制子尺寸(重点!)
勾选Height,代表:父 LayoutGroup 接管子物体高度,子物体 RectTransform 的高度直接被引擎锁死、不能手动拖拽改。
勾选之后,子多高多宽由父说了算,子自己 LayoutElement 尺寸规则失效。
Use Child Scale使用子缩放
是否把子物体 RectTransform 的 Scale 算进布局计算里。 一般不勾。如果你的子要动态缩放 UI,再开启,容易排版错乱。
Child Force Expand子强制扩展(弹性总开关,超级高频!)
Width:开启横向弹性通道。 勾选✔后:如果子没有自己设置 Flexible,布局默认给所有子 Flexible=1,平分剩余空间; 子自己 LayoutElement 写了 Flexible,就按写的权重瓜分剩余空白
不勾:Flexible 完全失效,不会自动撑满
Height:纵向弹性,一般很少开。
Vertical Layout Group 垂直布局组(纵向一列排列子物体)
参数和 Horizontal 几乎一模一样,只是排列方向从上往下。
Grid Layout Group 网格布局(格子布局,重点,和上面两个本质不一样!)
核心区别:Grid 不读取子的任何 Min/Preferred/Flexible,LayoutElement 尺寸设置无效!Grid 逻辑:我规定每个格子多大(Cell Size),所有子强制塞进格子里。
Padding:网格整体离父容器四边的内边距
Cell Size(网格核心)
每个格子的宽和高。这里是 100×100:所有子都会被强制放到 100×100 的格子里面。 子物体 Rect 会被 Grid 自动设置成这个尺寸,无视 Image/TMP/LayoutElement 想要的大小。
Spacing
格子与格子之间的间距:X 横向格子间距,Y 纵向格子间距。
Start Corner起始角落
从哪个角落开始放第一个子:Upper Left 左上 / Upper Right 右上 / Lower Left 左下 / Lower Right 右下。
Start Axis起始轴
Horizontal:优先先排满一行,再换行(一行放多个,再下一行,最常用)
Vertical:优先先排满一列,再换列(一列放满再往右开新列)
Child Alignment
整组网格在父容器里怎么对齐
和锚点有关系吗?
Grid Layout Group 不但不读子物体的锚点,还会强行把所有子物体的锚点改成同一个值——左上角
(0, 1)
由 GridLayoutGroup 驱动的部分参数
Constraint 约束模式(控制一行多少个 / 一列多少个,非常常用)
Flexible(你当前这个):自动适配根据父容器宽度,自动算一行能放下几个格子,父拉宽一行格子变多,父变窄一行变少。适合动态网格商品、图标。
Fixed Column Count:固定列数,比如填 3 →永远一行 3 个,不管父多宽,自动缩放格子间距 / 换行。
Fixed Row Count:固定行数,比如填 2 → 永远一共 2 行。
Grid 里面子身上的 LayoutElement:
Ignore Layout 正常生效:勾选后这个子不参与网格排版,自由摆放
Min / Preferred / Flexible 全部无效!Grid 强制 CellSize,不走那套分配算法。
Content Size Fitter(内容尺寸适配器)
ContentSizeFitter(简称 CSF)是让自己这个物体的 Rect 大小,去贴合它内部内容的 Preferred 尺寸
LayoutGroup(Horizontal/Vertical/Grid)是父管子:父来安排子的位置大小
CSF 是自己管自己:自己根据内部内容,自动改变自身宽高 它也是布局控制器
ILayoutController
Parent has a type of layout group component. A child of a layout group should not have a Content Size Fitter component, since it should be driven by the layout group.父物体是 LayoutGroup(Horizontal/Vertical/Grid),这个子物体不建议挂 ContentSizeFitter;子的尺寸应该由父 LayoutGroup 来驱动控制。
为什么 Unity 给这个警告?
这里出现了两个控制器抢着控制同一个 RectTransform
- 父:HorizontalLayoutGroup,它想决定这个子有多宽、放哪里(父 Layout 来分配空间)
- 子:ContentSizeFitter,它又想自己决定自己的宽高(我要贴合我内部内容) 两个布局引擎同时驱动同一个 Rect →布局循环冲突,容易抖动、计算错乱、编辑模式闪、Play 模式尺寸随机
分两种情况理解,不是绝对不能用,是容易踩坑
情况 A:不推荐、警告的结构
父:HorizontalLayoutGroup(父要分配子的尺寸) └──子:Image/TMP + ContentSizeFitter(子又想自己控制自己大小)Unity 警告。 能不能跑?有时候能跑,但是不稳定,布局循环风险很高,不推荐这么写。
情况 B:标准、安全、推荐的 CSF 用法,不会警告
CSF 放在LayoutGroup 自己身上!
物体A:VerticalLayoutGroup + ContentSizeFitter ├─子1 ├─子2 └─子3含义: 内部 Vertical 自动排列子;然后 CSF 让物体 A 的外框,自动包裹住里面所有子加起来的总大小。
这个是聊天气泡、动态弹窗最经典写法,没有警告,是正确用法。
那如果我 就是想要:Horizontal 里面有一个子,大小自适应自身内容怎么办?
正确做法: 子物体不要挂 CSF,靠子身上的 LayoutElement 的 Preferred 尺寸对外上报! 父 HorizontalLayoutGroup 读取子的 Preferred,自动适配这个子的大小,这才是 Horizontal/VerticalLayoutGroup 的设计逻辑。
TMP 自带 ILayoutElement,自带 Preferred 尺寸,直接给父 Horizontal 读,不需要子加 CSF。
CSF + LayoutElement 的搭配
同一个物体:ContentSizeFitter + LayoutElement
作用: CSF 默认跟着 Preferred 走;LayoutElement 可以给它加边界限制
例子: TMP + CSF (Horizontal=Preferred) + LayoutElement
- LayoutElement MinWidth=30,PreferredWidth=200 效果: CSF 包裹文字,但是宽度最小锁死 30;文字短的时候,框不会缩到比 30 更小。
这里是安全的,不会警告,因为这个物体外面没有 LayoutGroup 父。
4 种常用搭配速记
- TMP 聊天气泡:TMP + CSF Horizontal=Preferred, Vertical=Preferred (无 LayoutGroup 父)
- 自动包裹的容器:VerticalLayoutGroup + CSF Both Preferred (CSF 和 LayoutGroup 在同一个物体,标准写法)
- 横向一行,里面 TMP 文字自适应:父 HorizontalLayoutGroup,子 TMP不加 CSF,靠 TMP 原生 Preferred
- Grid 的子:绝对不要加 CSF,Grid 强制 CellSize,冲突爆炸