<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## drawFrame ~~~ 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 the OS. } ~~~ 在engine回調`window`的`onDrawFrame()`函數,`onDrawFrame()`里的是buildScope是build階段。接下來的函數`super.drawFrame()`里面的第一個調用`pipelineOwner.flushLayout()`就是layout階段。 ## pipelineOwner.flushLayout。 ~~~ void flushLayout() { while (_nodesNeedingLayout.isNotEmpty) { final List<RenderObject> dirtyNodes = _nodesNeedingLayout; _nodesNeedingLayout = <RenderObject>[]; for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) { if (node._needsLayout && node.owner == this) node._layoutWithoutResize(); } } } ~~~ ### _nodesNeedingLayout 1. 這里是按照在node tree中的深度順序遍歷\_nodesNeedingLayout 2. RenderObject的markNeedsLayout方法會把自己添加到\_nodesNeedingLayout 3. 最常見調用它的路徑是,setState的時候把某個Widget的child改成別的Widget。也就是只要某個Widget的child結點改變或者其他操作導致重新布局時,就會觸發調用markNeedsLayout。 ~~~ void markNeedsLayout() { if (_needsLayout) { return; } if (_relayoutBoundary != this) { markParentNeedsLayout(); // 會調用parent.markNeedsLayout } else { _needsLayout = true; if (owner != null) { owner._nodesNeedingLayout.add(this); owner.requestVisualUpdate(); } } } ~~~ 這個方法的處理邏輯就是: 1. 先判斷relayoutBoundary是不是自己,如果是就把自己存到\_nodesNeedingLayout,等待下一次Vsync對自己重新布局layout; 2. 如果relayoutBoundary不是自己,那就向上找問parent要relayoutBoundary。所以這是個遞歸,直到找到relayoutBoundary為止。 那么這段代碼作用就很明確了,那就是如果這個Widget有變動,它需要找到被它影響到的范圍,而這個范圍就是relayoutBoundary。然后需要對relayoutBoundary重新進行layout。 ### _layoutWithoutResize ~~~ void _layoutWithoutResize() { try { performLayout(); markNeedsSemanticsUpdate(); } catch (e, stack) { ... } _needsLayout = false; markNeedsPaint(); } ~~~ 1. 函數`performLayout()`需要其子類自行實現。因為有各種各樣的布局,就需要子類個性化的實現自己的布局邏輯。 ## performLayout ### RenderConstrainedBox `RenderPositionedBox`??的布局函數如下: ~~~ @override void performLayout() { if (child != null) { child.layout(constraints.loosen(), parentUsesSize: true); size = constraints.constrain(Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.infinity, shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.infinity)); alignChild(); } } ~~~ ### RenderDecoratedBox ~~~ @override void performLayout() { if (child != null) { child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true); size = child.size; } else { size = _additionalConstraints.enforce(constraints).constrain(Size.zero); } } ~~~ ## layout 在`performLayout()`的實現中。當有孩子節點的時候,一般會調用`child.layout()`請求孩子節點做布局。調用時要傳入對孩子節點的約束`constraints`,我們看一下`child.layout()`。這個函數在`RenderObject`類中: ~~~ void layout(Constraints constraints, { bool parentUsesSize = false }) { RenderObject relayoutBoundary; if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) { relayoutBoundary = this; } else { final RenderObject parent = this.parent; relayoutBoundary = parent._relayoutBoundary; } if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) { return; } _constraints = constraints; _relayoutBoundary = relayoutBoundary; if (sizedByParent) { try { performResize(); } catch (e, stack) { ... } } try { performLayout(); markNeedsSemanticsUpdate(); } catch (e, stack) { ... } _needsLayout = false; markNeedsPaint(); } ~~~ ### 第一步:確定relayoutBoundary 4個條件: 1. `parentUsesSize`:父組件是否需要子組件的尺寸,這是調用時候的入參,默認為`false`。 2. `sizedByParent`:這是個`RenderObject`的屬性,表示當前`RenderObject`的布局是否只受父`RenderObject`給與的約束影響。默認為`false`。子類如果需要的話可以返回`true`。比如`RenderErrorBox`。當我們的Flutter app出錯的話,屏幕上顯示出來的紅底黃字的界面就是由它來渲染的。 3. `constraints.isTight`:代表約束是否是嚴格約束。也就是說是否只允許一個尺寸。 4. 最后一個條件是父親節點是否是`RenderObject`。 在以上條件任一個滿足時,`relayoutBoundary`就是自己,否則取父節點的`relayoutBoundary`。 ### 第二步:盡量避免重新布局 如果當前節點不需要做重新布局,約束也沒有變化,`relayoutBoundary`也沒有變化就直接返回了。也就是說從這個節點開始,包括其下的子節點都不需要做重新布局了。這樣就會有性能上的提升。 ### 第三步 針對sizedByParent == true 如果`sizedByParent`為`true`,會調用`performResize()`。這個函數會僅僅根據約束來計算當前`RenderObject`的尺寸。當這個函數被調用以后,通常接下來的`performLayout()`函數里不能再更改尺寸了。 ### 第四步 performLayout `performLayout()`是大部分節點做布局的地方了。不同的`RenderObject`會有不同的實現。 ### 第五步 markNeedsPaint 最后標記當前節點需要被重繪 ## 總結 ### 核心思想 * 一層一層向下傳遞約束,直到葉子節點 * 子節點根據約束,確定自己的尺寸,然后把自己的尺寸向上傳遞
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看