2、Widget、Element、RenderObjectweb
3、Flutter UI 更新流程canvas
4、build 流程分析bash
9、Flutter 小實踐post
相似web 端 canvas, 咱們常常是採用分層的策略 例如如下游戲場景, 在打鬥的過程當中, 人物動做, 位移,動畫等變化的頻率和幅度是很大的,而背景變化的頻率或幅度則相對較小(基本不變,或者緩慢變化,或者僅在某些時機變化),這個過程須要很頻繁地更新和重繪人物,可是對於背景,咱們也許只須要繪製一次,也許只須要隔一段時間才重繪一次動畫
若是隻在一個畫布裏面繪製,那人物的的頻繁變化也會引發背景的繪製,所以須要須要生成多個畫布獨立繪製,最後再合成ui
在flutter 中, 也是採用分層繪製的概念,例以下圖,是Flutter框架渲染機制的一個示意圖~ 在框架渲染完成以後會輸出 的 一個個的 layer 造成的 layer tree,layer tree被送入engine,engine會把layer tree調度到GPU線程,在GPU線程內合成(compsite)layer tree,而後由Skia 2D渲染引擎渲染後送入GPU顯示~
(1) Layer
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
@override
ContainerLayer get parent => super.parent;
Layer get previousSibling => _previousSibling;
Layer _previousSibling;
}
複製代碼
類Layer是一個樹形結構, 屬性parent表明其父節點, nextSibling和previousSibling表示同一圖層的前一個和後一個兄弟節點,即圖層孩子節點們是用雙向鏈表存儲的
(2)AbstractNode
class AbstractNode {
int get depth => _depth;
int _depth = 0;
void redepthChildren() { }
Object get owner => _owner;
Object _owner;
/// The parent of this node in the tree.
AbstractNode get parent => _parent;
AbstractNode _parent;
void adoptChild(covariant AbstractNode child) {}
void dropChild(covariant AbstractNode child) {}
}
複製代碼
Layer 繼承 AbstractNode,所以ayer 也是一個個普通的節點,這個節點能夠是葉子節點,也能夠擁有子節點
Layer 有不一樣的實現類,只有ContainerLayer類型及其子類的圖層能夠擁有孩子,其餘類型的Layer子類都是葉子圖層。
分層繪製的核心思想是實現多個 canvas, 那在 flutter 裏面,何時會建立新的 canvas? needsCompositing
isRepaintBoundary
咱們先看 needsCompositing 在監聽 Vsync 信號調用 drawFrame, (1) drawFrame
@protected
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to
}
複製代碼
在調用 flushPaint 進行繪製以前,會先調用 flushCompositingBits
(2) flushCompositingBits
void flushCompositingBits() {
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
}
複製代碼
這個函數中,主要是遍歷 _nodesNeedingCompositingBitsUpdate 節點集合,調用 _updateCompositingBits
(3) _updateCompositingBits
void _updateCompositingBits() {
if (!_needsCompositingBitsUpdate)
return;
final bool oldNeedsCompositing = _needsCompositing;
_needsCompositing = false;
visitChildren((RenderObject child) {
child._updateCompositingBits();
if (child.needsCompositing)
_needsCompositing = true;
});
if (isRepaintBoundary || alwaysNeedsCompositing)
_needsCompositing = true;
if (oldNeedsCompositing != _needsCompositing) {
// 若是 oldNeedsCompositing != _needsCompositing, 則說明節點所在的圖層發生了改變,則須要從新繪製
markNeedsPaint();
}
_needsCompositingBitsUpdate = false;
}
複製代碼
在這個函數中,遍歷子節點,若是子節點 的 needsCompositing 爲true, 則須要將該節點的 needsCompositing 設置爲 true
上述代碼 (2) 中,_nodesNeedingCompositingBitsUpdate 節點集合怎麼來的? 追蹤代碼、則發如今 markNeedsCompositingBitsUpdate 函數中有相關處理
(4) markNeedsCompositingBitsUpdate
void markNeedsCompositingBitsUpdate() {
if (_needsCompositingBitsUpdate)
return;
_needsCompositingBitsUpdate = true;
if (parent is RenderObject) {
final RenderObject parent = this.parent;
if (parent._needsCompositingBitsUpdate)
return;
if (!isRepaintBoundary && !parent.isRepaintBoundary) {
parent.markNeedsCompositingBitsUpdate();
return;
}
}
if (owner != null)
owner._nodesNeedingCompositingBitsUpdate.add(this);
}
複製代碼
在該代碼段中會將該 renderObject 加入 _nodesNeedingCompositingBitsUpdate 集合中,同時向上遍歷父節點的 markNeedsCompositingBitsUpdate 方法,
(5)markNeedsCompositingBitsUpdate 什麼時候調用? renderObject 通常在 添加,刪除孩子時調用 markNeedsCompositingBitsUpdate
//添加孩子
@override
void adoptChild(RenderObject child) {
markNeedsCompositingBitsUpdate();
}
//刪除孩子
@override
void dropChild(RenderObject child) {
super.dropChild(child);
markNeedsCompositingBitsUpdate();
}
複製代碼
needsCompositing有哪些應用場景
(6) needsCompositing
在繪製時,若是 needsCompositing 這個屬性值 爲 true, 則意味着須要新增一個 layer
ClipRectLayer pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer oldLayer }) {
final Rect offsetClipRect = clipRect.shift(offset);
if (needsCompositing) {
final ClipRectLayer layer = oldLayer ?? ClipRectLayer();
layer
..clipRect = offsetClipRect
..clipBehavior = clipBehavior;
pushLayer(layer, painter, offset, childPaintBounds: offsetClipRect);
return layer;
} else {
clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset));
return null;
}
}
複製代碼