performTraversals
和前面分析測量過程相似,整個佈局的起點也是在ViewRootImpl
的performTraversals
當中:bash
private void performTraversals() {
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
......
}
複製代碼
能夠看到,佈局過程會參考前面一步測量的結果,和測量過程的measure
和onMeasure
方法很像,佈局過程也有兩個方法layout
和onLayout
,參考前面的分析,咱們先對這兩個方法進行介紹:ide
layout
和onLayout
View
layout
方法是public
的,和measure
不一樣的是,它不是final
的,也就是說,繼承於View
的控件能夠重寫layout
方法,可是咱們通常不這麼作,由於在它的layout
方法中又調用了onLayout
,因此繼承於View
的控件通常是經過重寫onLayout
來實現一些邏輯。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
方法是一個空實現。protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
複製代碼
ViewGroup
View
中的layout
,並把它設爲final
,也就是說繼承於ViewGroup
的控件,不能重寫layout
,在layout
方法中,又會調用super.layout
,也就是View
的layout
。@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
的控件,都必須實現onLayout
方法。@Override
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
複製代碼
View
的控件 例如TextView
,咱們通常不會重寫layout
,而是在onLayout
中進行簡單的處理。ViewGroup
的控件LinearLayout
,因爲ViewGroup
的做用是爲了包裹子View
,而每一個控件因爲做用不一樣,佈局的方法天然也不一樣,這也是爲了安卓要求每一個繼承於ViewGroup
的控件都必須實現onLayout
方法的緣由。ViewGroup
的layout
方法不能夠重寫,所以,當咱們經過父容器調用一個繼承於ViewGroup
的控件的layout
方法時,它最終會回調到該控件的onLayout
方法。onLayout(boolean changed, int l, int t, int r, int b)
參數說明當onLayout
方法被回調時,傳入了上面這四個參數,通過前面的分析,咱們知道onLayout
是經過layout
方法調用過來,而layout
方法父容器調用的,父容器在調用的時候是根據本身的座標來計算出寬高,並把本身的位置的左上角看成是(0,0)
點,從新決定它所屬子View
的座標,所以這個矩形的四個座標是相對於父容器的座標值。函數
雖然layout
在某些方面和measure
有所不一樣,可是它們有一點是共通的,那就是:它們都是做爲整個從根節點到葉節點傳遞的紐帶,當從父容器到子View
傳遞的過程當中,咱們不直接調用onLayout
,而是調用layout
。 onMeasure
在測量過程當中負責兩件事:它本身的測量和它的子View
的測量,而onLayout
不一樣:它並不負責本身的佈局,這是由它的父容器決定的,它僅僅負責本身的下一級子View
的佈局。 再回到文章最開始的點,起點是經過mView
也就是DecorView
的layout
方法觸發的,而DecorView
其實是一個FrameLayout
,通過前面的分析,咱們應該直接去看FrameLayout
的onLayout
方法:oop
@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
當中,又調用了它的子View
的layout
,那麼這時候就分爲兩種狀況,一種是該child
是繼承於ViewGroup
的控件而且它有子節點,那麼child.layout
方法最終又會調用到child.onLayout
,在裏面,它一樣會進行和FrameLayout
所相似的操做,繼續調用child
的子節點的layout
;另外一種是該child
是View
或者是繼承於View
的控件或者是它是繼承於ViewGroup
的控件可是沒有子節點,那麼到該child
節點的佈局遍歷過程就結束了。佈局
經過分析測量和佈局的過程,它們基於一個思想,把傳遞和實現這兩個邏輯分開在不一樣的函數中處理,在實現當中,再去決定是否要傳遞。ui