View 繪制體系知識梳理(4) - 繪制過程之 Layout 詳解

一、布局的起點(diǎn) - performTraversals

和前面分析測量過程類似,整個(gè)布局的起點(diǎn)也是在ViewRootImplperformTraversals當(dāng)中:

private void performTraversals() {
    ......
    mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
    ......
}

可以看到,布局過程會參考前面一步測量的結(jié)果,和測量過程的measureonMeasure方法很像,布局過程也有兩個(gè)方法layoutonLayout,參考前面的分析,我們先對這兩個(gè)方法進(jìn)行介紹:

1.1 layoutonLayout

  • 對于View
  • layout方法是public的,和measure不同的是,它不是final的,也就是說,繼承于View的控件可以重寫layout方法,但是我們一般不這么做,因?yàn)樵谒?code>layout方法中又調(diào)用了onLayout,所以繼承于View的控件一般是通過重寫onLayout來實(shí)現(xiàn)一些邏輯。
     public void layout(int l, int t, int r, int b) {
         //....
         boolean changed = isLayoutModeOptical(mParent) ?
                 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
         if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
             onLayout(changed, l, t, r, b);
         }
         //...
     }
    
  • onLayout方法是一個(gè)空實(shí)現(xiàn)。
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
  • 對于ViewGroup
  • 它重寫了View中的layout,并把它設(shè)為final,也就是說繼承于ViewGroup的控件,不能重寫layout,在layout方法中,又會調(diào)用super.layout,也就是Viewlayout
    @Override
    public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b);
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
    }
    
  • onLayout方法重寫View中的onLayout方法,并把它聲明成了abstract,也就是說,所有繼承于ViewGroup的控件,都必須實(shí)現(xiàn)onLayout方法
    @Override
    protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
  • 對于繼承于View的控件
    例如TextView,我們一般不會重寫layout,而是在onLayout中進(jìn)行簡單的處理。
  • 對于繼承于ViewGroup的控件
  • 例如LinearLayout,由于ViewGroup的作用是為了包裹子View,而每個(gè)控件由于作用不同,布局的方法自然也不同,這也是為了安卓要求每個(gè)繼承于ViewGroup的控件都必須實(shí)現(xiàn)onLayout方法的原因。
  • 因?yàn)?code>ViewGroup的layout方法不可以重寫,因此,當(dāng)我們通過父容器調(diào)用一個(gè)繼承于ViewGroup的控件的layout方法時(shí),它最終會回調(diào)到該控件的onLayout方法。

1.2 布局onLayout(boolean changed, int l, int t, int r, int b)參數(shù)說明

當(dāng)onLayout方法被回調(diào)時(shí),傳入了上面這四個(gè)參數(shù),經(jīng)過前面的分析,我們知道onLayout是通過layout方法調(diào)用過來,而layout方法父容器調(diào)用的,父容器在調(diào)用的時(shí)候是根據(jù)自己的坐標(biāo)來計(jì)算出寬高,并把自己的位置的左上角當(dāng)作是(0,0)點(diǎn),重新決定它所屬子View的坐標(biāo),因此這個(gè)矩形的四個(gè)坐標(biāo)是相對于父容器的坐標(biāo)值

1.3 布局的遍歷過程

雖然layout在某些方面和measure有所不同,但是它們有一點(diǎn)是共通的,那就是:它們都是作為整個(gè)從根節(jié)點(diǎn)到葉節(jié)點(diǎn)傳遞的紐帶,當(dāng)從父容器到子View傳遞的過程中,我們不直接調(diào)用onLayout,而是調(diào)用layout
onMeasure在測量過程中負(fù)責(zé)兩件事:它自己的測量和它的子View的測量,而onLayout不同:它并不負(fù)責(zé)自己的布局,這是由它的父容器決定的,它僅僅負(fù)責(zé)自己的下一級子View的布局。
再回到文章最開始的點(diǎn),起點(diǎn)是通過mView也就是DecorViewlayout方法觸發(fā)的,而DecorView實(shí)際上是一個(gè)FrameLayout,經(jīng)過前面的分析,我們應(yīng)該直接去看FrameLayoutonLayout方法:

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

可以看到,在它的onLayout當(dāng)中,又調(diào)用了它的子Viewlayout,那么這時(shí)候就分為兩種情況,一種是該child是繼承于ViewGroup的控件并且它有子節(jié)點(diǎn),那么child.layout方法最終又會調(diào)用到child.onLayout,在里面,它同樣會進(jìn)行和FrameLayout所類似的操作,繼續(xù)調(diào)用child的子節(jié)點(diǎn)的layout;另一種是該childView或者是繼承于View的控件或者是它是繼承于ViewGroup的控件但是沒有子節(jié)點(diǎn),那么到該child節(jié)點(diǎn)的布局遍歷過程就結(jié)束了。

1.4 小結(jié)

通過分析測量和布局的過程,它們基于一個(gè)思想,把傳遞實(shí)現(xiàn)這兩個(gè)邏輯分開在不同的函數(shù)中處理,在實(shí)現(xiàn)當(dāng)中,再去決定是否要傳遞

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容