layout

Sublinear layout

flutter 会在每一帧执行一次布局操作,布局计算仅在一次传递中完成。约束通过父节点调用每个子节点的布局方法的方式向下传递。子节点递归执行自己的布局操作,通过布局方法返回携带几何信息的渲染对象。一旦渲染对象从布局方法中返回,直到下一帧布局操作都不会被两次访问。这种策略将原本可能独立的测量传递和布局传递合并为单次传递。因此,每个渲染对象在布局过程中最多被访问两次:一次在树的向下传递过程中,一次是树的向上传递过程中。 flutter具有这种通用协议的多种实现。最常用的是RenderBox,运行在二维笛卡尔坐标系中。在盒子布局中,约束是最小和最大宽高。在布局过程中,子节点通过选择一个满足约束的大小来确定自己的几何信息。子节点布局返回后,由父节点决定子节点在父坐标系中的位置。注意,子节点布局不取决于它的位置,因为它的位置直到它从布局中返回后才确定。因此父节点可以重新定位子节点,而无需重新计算子节点布局。

更广泛地讲,在布局期间,从父节点流向子节点的唯一信息是约束信息,从子节点流向父节点的唯一信息是几何信息。通过这些不变量可减少布局期间所需的工作量

  • 如果子节点没有被标记为`dirty,只要父节点传递给子节点的约束与上次相同,那么子节点可以在布局时立即返回。

  • 如果父节点不使用子节点返回的大小信息,那么如果子节点重新选择了大小父节点也无需重新计算布局。

  • 严格约束指只有一个有效的几何信息满足约束。如果父节点提供了严格约束,那么当子节点重新计算布局时父节点无需重新计算自己的布局,尽管父节点在布局时使用了子节点的大小,因为子节点在没有接收到新约束时不能改变自己的大小。

  • 渲染对象可以声明它只使用父节点提供的约束来确定自己的几何信息。即使约束不严格且父节点的布局依赖于子节点的大小,当子节点重新计算布局时,父节点也无需重新计算其布局时,因为子节点在没有接收新的约束条件下无法更改其大小。

几何:研究空间结构与性质的学科

Sublinear widget building

...

RenderBox

RenderProxyBox

默认情况下,RenderProxyBox渲染对象调整自身大小以适合其子节点,如果没有子节点则尽可能小;它传递所有的命中测试(hit testing)和绘制给子节点,固有尺寸和基线测量同样转发给子节点。 RenderProxyBox假定子代与父代大小相同并且子节点位于0,0

RenderShiftedBox

默认情况下,RenderShiftedBox的行为与RenderProxyBox相似,但是不假定子节点位于0,0,和不提供默认的布局逻辑

子节点种类和特定于子节点的数据

RenderBox的子节点可以是不限于RenderBoxRenderObject的子类

子代使用parentData字段来存储父代的数据。这个数据必须继承于ParentData类。setupParentData方法用于初始化parentData

BoxParentData.offset字段存储子代相对父代的位置

Layout

RenderBox类实现了布局算法。它们具有一级约束,并基于这些约束和可能具有的其它任何输入(例如:其的子代或属性)来调整自身的大小。

在实现RenderBox子类时,必须做出选择。是仅基于约束来确定自身的大小还是使用其它信息。完全基于约束的子类将会变大以适合父代。

完全基于约束调整大小允许系统进行重大优化。使用此方法的类需要重写sizedByParent返回true,并重写performResize仅使用约束来设置size

@override
bool get sizedByParent => true;

@override
void performResize() {
  size = constraints.smallest;
}

否则,通过performLayout方法设置大小

performLayout是渲染框决定size(如果不是sizedByParent),和决定子代位置的地方

Layout of RenderBox children

performLayout方法需要调用每个子代的layout方法,并传递描述子代可以在哪里渲染的BoxConstraints对象给它。传递严格约束(BoxConstraints.isTight)给子代允许渲染库应用一些优化,因为它知道如果约束是严格的,子代的尺寸不会因为子代布局的变化而改变。

如果performLayout方法将使子代的大小来影响布局的其它方面,例如如果渲染框围绕子代调整大小,或根据子代的大小来设置这些子代的位置,那么必须设置layout方法的parentUsesSize参数为true

这个标志关闭了一些优化;不依赖于子代大小的算法将更高效。(依赖于子代的size意味着如果子代被标志为dirty,父代也可能将被标志为dirty,除了父代传递给子代的constraints是严格约束)

对于不继承自RenderProxyBoxRenderBox类,一旦它们布局了它们的子代,便也需要通过设置BoxParentData.offset字段给每个子代的parentData对象来定位它们。

Layout of no-RenderBox children

...

Painting

要描述渲染框是如何绘制的,需要实现paint方法。

Last updated