轉自 http://blog.csdn.net/leehong2005/article/details/7299471
正常情況下,我們重寫LinearLayout的onDraw方法,它是不會被調用的,這篇文章就來分析一下原因和解決方法。
一,現象
<com.test.demo.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_absolute"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000000">
</com.test.demo.MyLinearLayout>
大概的架構是,MyLinearLayout從LinearLayout派生出來,然後在程序中重載OnDraw(Canvas canvas)。但是,onDraw不會被調用。我們可能會遇到這個問題:如果不給LinearLayout設置一個背景,系統是不會調用onDraw時,也就是說,我們重寫的onDraw是不會調用的。當設置一個背景後,onDraw就會被調用。
二,原因
造成這種現象的原因是繼承自LinearLayout,而LinearLayout這是一個容器,ViewGroup嘛,它本身並沒有任何可畫的東西,它是一個透明的控件,因些並不會觸發onDraw,但是你現在給LinearLayout設置一個背景色,其實這個背景色不管你設置成什麼顏色,系統會認爲,這個LinearLayout上面有東西可畫了,因此會調用onDraw方法。
我們可以仔細分析View的源碼,它有一個方法View#draw(Canvas)方法,這裏面有兩個地方調用onDraw,它的條件都是:
if (!dirtyOpaque) onDraw(canvas);
也就是說,如果dirtyOpaque是true的話,onDraw就不會調用,而dirtyOpaque的值的計算代碼如下:
- final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
- (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
- final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
- (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
// Opaque if:
// - Has a background
// - Background is opaque
// - Doesn't have scrollbars or scrollbars are inside overlay
View還提供了一個重要的方法:setWillNotDraw,我們看一看它的實現:
- /**
- * If this view doesn't do any drawing on its own, set this flag to
- * allow further optimizations. By default, this flag is not set on
- * View, but could be set on some View subclasses such as ViewGroup.
- *
- * Typically, if you override {@link #onDraw} you should clear this flag.
- *
- * @param willNotDraw whether or not this View draw on its own
- */
- public void setWillNotDraw(boolean willNotDraw) {
- setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
- }
- /**
- * If this view doesn't do any drawing on its own, set this flag to
- * allow further optimizations. By default, this flag is not set on
- * View, but could be set on some View subclasses such as ViewGroup.
- *
- * Typically, if you override {@link #onDraw} you should clear this flag.
- *
- * @param willNotDraw whether or not this View draw on its own
- */
- public void setWillNotDraw(boolean willNotDraw) {
- setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
- }
從這個方法的註釋,我們可以看出,如果你想重寫onDraw的話,你應該調用這個方法來清除flag,所以如果我們想要重寫LinearLayout的onDraw的話,我們也可以在其構造方法中調用setWillNotDraw方法。 在ViewGroup初始他時,它調用了一個私有方法:initViewGroup,它裏面會有一句setFlags(WILL_NOT_DRAW, DRAW_MASK); 相當於調用了setWillNotDraw(true),所以說,對於ViewGroup,它就認爲是透明的了。如果我們想要重寫onDraw,就需要調用setWillNotDraw(false)
三,總結一下:
1)ViewGroup默認情況下,會被設置成WILL_NOT_DRAW,這是從性能考慮,這樣一來,onDraw就不會被調用了。
2)如果我們要重要一個ViweGroup的onDraw方法,有兩種方法:
1,在構造函數裏面,給其設置一個顏色,如#00000000。
2,在構造函數裏面,調用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。