2、Widget、Element、RenderObjectcanvas
4、build 流程分析app
8、composite 流程分析post
第六章节简单介绍了 图层,并分析了影响图层建立的元素之一 needsCompositing, 这一章节会重点介绍整个绘制流程。this
(1) flushPaintspa
void flushPaint() {
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
}
复制代码
上述代码主要是两个步骤 给节点排序 遍历 _nodesNeedingPaint 判断 layer.attached, 若是为为true, 则调用 repaintCompositedChild(node) 去作绘制,不然调用node._skippedPaintingOnLayer
(2) markNeedsPaint
void markNeedsPaint() {
assert(owner == null || !owner.debugDoingPaint);
if (_needsPaint)
return;
_needsPaint = true;
if (isRepaintBoundary) {
//若是当前节点 isRepaintBoundary 为 true, 则将该节点添加到 _nodesNeedingPaint结合中
if (owner != null) {
owner._nodesNeedingPaint.add(this);
owner.requestVisualUpdate();
}
} else if (parent is RenderObject) {
// 向上层层遍历父节点,直至将最近 isRepaintBoundary 为true 的父节点添加到 _nodesNeedingPaint
final RenderObject parent = this.parent;
parent.markNeedsPaint();
} else {
// 根节点
if (owner != null)
owner.requestVisualUpdate();
}
}
复制代码
因而可知, 重绘节点集合中只有 isRepaintBoundary 为 true 的 renderObject, 和 relayoutBoundary 相似,该属性决定这个RenderObject 重绘时是否独立于其父节点,若是该属性值为true ,则独立绘制,反之则一块儿绘制(那若是是普通节点没有加入 dirtyNodes集合, 它们怎么绘制的呢?)
(3) _repaintCompositedChild
static void _repaintCompositedChild(
RenderObject child, {
bool debugAlsoPaintedParent = false,
PaintingContext childContext,
}) {
OffsetLayer childLayer = child._layer;
//1
if (childLayer == null) {
child._layer = childLayer = OffsetLayer();
} else {
childLayer.removeAllChildren();
}
//2 绘制上下文
childContext ??= PaintingContext(child._layer, child.paintBounds);
//三、绘制
child._paintWithContext(childContext, Offset.zero);
childContext.stopRecordingIfNeeded();
}
复制代码
注释1: 会先判断节点的 layer 是否为null,若是是 null, 则为改节点建立一个新的layer, 若是不为空,则须要清空该layer上的全部孩子节点(因为只有 isRepaintBoundary 的 renderObject 才在 dirtyNodes 集合中,所以不是全部的 renderObject 都拥有一个 layer) 注释2:初始化绘制的上下文 ,这个相似 canvas 上下文 注释3:调用节点的 _paintWithContext 进行绘制
(4) _paintWithContext
void _paintWithContext(PaintingContext context, Offset offset) {
paint(context, offset);
}
复制代码
这个方法其实简单调用了 paint
(5) paint
paint 方法 是由具体的实现类去重写覆盖的, 如下面 xx 类为例
@override
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChild(child, offset);
}
复制代码
(6) paintChild
void paintChild(RenderObject child, Offset offset) {
if (child.isRepaintBoundary) {
_compositeChild(child, offset);
} else {
child._paintWithContext(this, offset);
}
}
复制代码
绘制主要分为两种状况: 第一种状况:有绘制边界,即 isRepaintBoundary 为 true时,则从新生成图层和重绘, 往下第(7)点 第二种状况: 普通绘制,往下第 (17)点
(7)_compositeChild
void _compositeChild(RenderObject child, Offset offset) {
if (child._needsPaint) {
// 重绘该节点
repaintCompositedChild(child, debugAlsoPaintedParent: true);
}
// 新增图层,并将该图层 append 到 父图层
final OffsetLayer childOffsetLayer = child._layer;
childOffsetLayer.offset = offset;
appendLayer(child._layer);
}
复制代码
若是节点须要重绘,则绘制该节点,同时生成新的 layer, 并将新的 layer 加入 父图层
(8) appendLayer 这个方法调用 containerLayer 的 append 方法,将新增的图层加入父图层
@protected
void appendLayer(Layer layer) {
layer.remove();
_containerLayer.append(layer);
}
复制代码
(9) append 调用 adoptChild 方法设置新的 layer 的 owner 值,同时经过设置 _previousSibling 和 _nextSibling 将该图层节点加入图层链表
void append(Layer child) {
adoptChild(child);
child._previousSibling = lastChild;
if (lastChild != null)
lastChild._nextSibling = child;
_lastChild = child;
_firstChild ??= child;
}
复制代码
(10) adoptChild
void adoptChild(AbstractNode child) {
child._parent = this;
if (attached)
child.attach(_owner); // owner 只是一个对象,整个子树的各个节点应该拥有相同的 owner, owner
}
复制代码
(11) child.attach
@mustCallSuper
void attach(covariant Object owner) {
_owner = owner;
}
复制代码
由上面可知, 在添加新的 layer 时,会调用子 layer 的 attach 方法将父 layer 的 owner 传给子 layer
那何时会重置 layer 的 owner 值呢? 在移除 子 layer 的时候
(12) removeAllChildren 在 _repaintCompositedChild 函数中,咱们能够看到 layer 在从新绘制时,若是这个 layer 不为空,会先调用removeAllChildren 清空这个layer 的全部的子节点
void removeAllChildren() {
Layer child = firstChild;
while (child != null) {
final Layer next = child.nextSibling;
child._previousSibling = null;
child._nextSibling = null;
dropChild(child);
child = next;
}
_firstChild = null;
_lastChild = null;
}
复制代码
这个方法将 该节点 _previousSibling,_nextSibling 设置为空, 同时调用 dropChild
(13) dropChild 经过调用 图层节点的 detach 方法 重置节点的owner值
void dropChild(covariant AbstractNode child) {
child._parent = null;
if (attached)
child.detach();
}
复制代码
dropChild 函数中又调用了 dettach
(14) dettach 将 owner 值设置为空
@mustCallSuper
void detach() {
_owner = null;
}
复制代码
最初始的 owner 是什么呢?
(15) RenderView -> scheduleInitialFrame
void scheduleInitialFrame() {
scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
}
复制代码
(16) _updateMatricesAndCreateNewRootLayer
Layer _updateMatricesAndCreateNewRootLayer() {
final ContainerLayer rootLayer = TransformLayer(transform: _rootTransform);
rootLayer.attach(this);
return rootLayer;
}
复制代码
此时 owner 指向 this, 也就是 renderView
(17)普通绘制,以 _SelectToggleButtonRenderObject 为例
void paint(PaintingContext context, Offset offset) {
super.paint(context, offset);
switch (textDirection) {
case TextDirection.ltr:
final Path leadingPath = Path()
..moveTo(outer.right, rrect.bottom)
..lineTo(rrect.left + rrect.blRadiusX, rrect.bottom)
..addArc(blCorner, math.pi / 2.0, sweepAngle)
..lineTo(rrect.left, rrect.top + rrect.tlRadiusY)
..addArc(tlCorner, math.pi, sweepAngle)
..lineTo(outer.right, rrect.top);
context.canvas.drawPath(leadingPath, leadingPaint);
case TextDirection.rtl:
break;
}
}
复制代码
使用PaintingContext的画布canvas来绘制路径等等,这里的绘制都是在一个 PictureLayer 的图层上所作的, 在初始化paintContext时,会生成一个默认的图层 PictureLayer
@override
Canvas get canvas {
if (_canvas == null)
_startRecording();
return _canvas;
}
void _startRecording() {
_currentLayer = PictureLayer(estimatedBounds);
_recorder = ui.PictureRecorder();
_canvas = Canvas(_recorder);
_containerLayer.append(_currentLayer);
复制代码