布局与约束
DirectSurface UI 的布局采用约束传递模型。父组件决定子组件能用多大空间,子组件在约束范围内返回自己的尺寸,父组件再决定子组件的位置。
基本流程
一次布局通常包含三步:
- 父组件把约束传给子组件。
- 子组件根据约束和自身内容计算
size。 - 父组件设置子组件的
offset。
这意味着子组件不能随意决定自己出现在页面上的位置。位置由父组件决定。
约束含义
约束通常包含:
- 最小宽度。
- 最大宽度。
- 最小高度。
- 最大高度。
如果最大宽度或最大高度是有限值,说明父组件希望子组件在这个范围内布局。如果是无限值,说明子组件可以按内容自然扩展。
常见布局关系
RenderStackPanel 会按方向顺序排列子组件,并根据权重分配剩余空间。
RenderDockPanel 会先布局停靠在上下左右的区域,剩余区域交给 fill 子组件。
RenderGridPanel 会按行列轨道分配空间,适合表单和结构化页面。
RenderScrollViewer 会给内容一个可滚动视窗,内容可以大于视窗。
何时需要重新布局
影响尺寸或位置的状态变化,需要触发布局:
markNeedsLayout()
典型场景:
- 子节点数量变化。
- 文本变化导致测量结果变化。
- 展开或折叠内容。
- 列宽、行高、面板尺寸变化。
- 虚拟列表可视范围变化。
只影响颜色、hover、光标闪烁、选中背景等视觉状态时,通常只需要重绘。
性能原则
- 布局中不要递归扫描不可见的大量节点。
- 大列表、大树和大文档应只布局可视区域或增量区域。
- resize 期间避免反复创建组件对象。
- 不要在布局阶段调用外部业务请求或触发复杂状态变更。
常见错误
- 子组件自己改全局坐标。
- 在 paint 中修改会触发布局的状态。
- 每次布局都重建整棵子树。
- 外层和内层同时管理滚动,导致虚拟化失效。