Composition过程

经过Build、Layout、Paint后,Render Tree变成Layer Tree,那么Layer Tree是如何合成,以变成最终的渲染数据呢?这就是Composition过程。

Mark阶段

Framework使用_neesaAddToScene字段标识当前图层是否需要进行合成,通常当一个Layer节点有子节点的变化(adoptChild、dropChild)或者Layer节点本身有变化时,需要将该标识设置为true,表示当前图层发生改变,需要重新合成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Layer {
set alpha(int? value) {
if (value != _alpha) {
if (value == 255 || _alpha == 255) {
engineLayer = null;
}
_alpha = value;
markNeedsAddToScene();
}
}

set elevation(double? value) {
if (value != _elevation) {
_elevation = value;
markNeedsAddToScene();
}
}

void markNeedsAddToScene() {
if (_needsAddToScene) {
return;
}
_needsAddToScene = true;
}

}

Flush阶段

合成的Flush阶段是从renderView.compositeFrame方法开始

Read More

Box布局模型

Lyaout的本质是父节点向子节点传递自己的布局约束Constraints,子节点计算自身的大小(Size),父节点再根据大小信息计算偏移(Offset)。在二维空间中,根据大小和偏移可以唯一确定子节点的位置。

Flutter中主要存在两种布局约束,Box和Sliver:

BoxConstraints和SliverConstraints分别对应Box布局和Sliver布局模型所需要的约束条件。ParentData是RenderObject持有的一个字段,用于为父节点提供额外的信息。比如RenderBox通过BoxParentData向父节点暴露自身的偏移量。

Box布局概述

Read More

Layout过程

1
2
3
4
5
6
7
8
9
10
11
12
13
class RenderBinding {
void drawFrame() {
pipelineOwner.flushLayout();//布局
pipelineOwner.flushCompositingBits();//更新所有节点,计算待绘制区域数据
pipelineOwner.flushPaint();//绘制
if (sendFramesToEngine) {
renderView.compositeFrame(); // 发送数据到GPU线程
pipelineOwner.flushSemantics(); // 更新语义化
_firstFrameSent = true;
}
}
}

过程

标记阶段

markNeedsLayout方法会将当前节点标记为需要Layout。markNeedsLayout方法在很多地方都会被触发,比如UI的高宽发生变化,字体属性发生变化等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class RenderObject {
void markNeedsLayout() {
if (_needsLayout) {//已经标记过
return;
}
if (_relayoutBoundary == null) {//当前节点不是布局边界,父节点受此影响,也需要被标记
_needsLayout = true;
if (parent != null) {
markParentNeedsLayout();
}
return;
}
if (_relayoutBoundary != this) {
markParentNeedsLayout();
} else {
_needsLayout = true;
if (owner != null) {
owner!._nodesNeedingLayout.add(this);
owner!.requestVisualUpdate();//请求刷新
}
}
}

void markParentNeedsLayout() {
_needsLayout = true;
final RenderObject parent = this.parent! as RenderObject;
if (!_doingThisLayoutWithCallback) {
parent.markNeedsLayout();
} else {
}
}

}
Read More