View的layout過程
下面是表達(dá)其大概思路的偽碼
public void layout(int l, int t, int r, int b){
setFrame(l ,t , r, b);
onLayout(changed ,l , t, r, b);
}
上面的思路也很清晰,就是首先傳入了l ,t, r, b四個參數(shù),然后調(diào)用setFrame(l, t, r, b)
方法,通過setFrame()
方法來設(shè)定View的四個頂點的位置,這樣就設(shè)定了View在父容器中的位置。接著就調(diào)用onLayout()
方法,這個方法的用途就是父容器確定其中所有子View的位置。有個地方需要注意:layout(int l, int t, int r, int b)
中這四個參數(shù)是誰傳入的?其實,就父View的onLayout()
方法中調(diào)用子view的layout()
方法時傳入的。
同樣,由于onLayout()
的具體實現(xiàn)和具體的布局有關(guān),所以View和ViewGroup均沒有真正地實現(xiàn)onLayout()
方法,總體來說,偽碼實現(xiàn)如下:
protected void onLayout(boolean changed, int l, int t, int r, int b){
for (int i = 0; i < count; i ++){
View child = getVirtualChildAt(i);
...... // 獲取對應(yīng)child的childLeft, childTop , childRight, childBottom數(shù)據(jù)
child.layout(childLeft, childTop , childRight, childBottom); //注解一
}
}
注解一:
注意這里的childLeft, childTop , childRight, childBottomd都是需要根據(jù)具體情況計算而來的。
View的draw過程
View的draw過程比較簡單,下面使用偽碼來表明整個過程
public void draw(Canvas canvas){
...
//step 1 繪制背景
drawBackground(canvas);
//step 2 繪制自己
onDraw(canvas);
//step 3 繪制子View
dispatchDraw(canvas); //注解一
//step 4 繪制裝飾
onDrawScrollBars(canvas);
...
return;
}
//注解一
View繪制過程的傳遞是通過dispatchDraw()
來實現(xiàn)的,dispatchDraw()
會遍歷所有的子元素的draw()
方法,如此draw事件就一層層地傳遞下去了。
還有一個地方需要注意,View有一個特殊的方法setWillNotDraw()
:
public void setWillNotDraw(boolean willNotDraw){
setFlags(willNotDraw ? WILL_NOT_DRAW : 0 , DRAW_MASK);
}
如果一個View不需要繪制任何內(nèi)容,那么設(shè)置這個標(biāo)志位為true以后,系統(tǒng)就會進(jìn)行相應(yīng)的優(yōu)化。默認(rèn)情況下,View沒有啟用這個標(biāo)志位,但是ViewGroup會默認(rèn)啟用這個優(yōu)化標(biāo)志位。這個標(biāo)志位的實際開發(fā)意義:當(dāng)我們自定義控件繼承ViewGroup并且本身不具備繪制功能時,就可以開啟這個標(biāo)記位從而便于系統(tǒng)進(jìn)行后續(xù)的優(yōu)化。如果知道一個ViewGroup需要通過onDraw()
來繪制自身內(nèi)容時,我們需要顯式地關(guān)閉WILL_NOT_DRAW這個標(biāo)記位。