ViewGroup為什麼不會呼叫onDraw

iteye_21202發表於2013-05-03

目錄(?)[+]

正常情況下,我們重寫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的值的計算程式碼如下:

  1. finalbooleandirtyOpaque=(privateFlags&DIRTY_MASK)==DIRTY_OPAQUE&&
  2. (mAttachInfo==null||!mAttachInfo.mIgnoreDirtyState);
當View初始他時,它會呼叫一個私有方法:computeOpaqueFlags,這裡面列出了不透明的三個條件:

// Opaque if:

// - Has a background

// - Background is opaque

// - Doesn't have scrollbars or scrollbars are inside overlay

View還提供了一個重要的方法:setWillNotDraw,我們看一看它的實現:

  1. /**
  2. *Ifthisviewdoesn'tdoanydrawingonitsown,setthisflagto
  3. *allowfurtheroptimizations.Bydefault,thisflagisnotseton
  4. *View,butcouldbesetonsomeViewsubclassessuchasViewGroup.
  5. *
  6. *Typically,ifyouoverride{@link#onDraw}youshouldclearthisflag.
  7. *
  8. *@paramwillNotDrawwhetherornotthisViewdrawonitsown
  9. */
  10. publicvoidsetWillNotDraw(booleanwillNotDraw){
  11. setFlags(willNotDraw?WILL_NOT_DRAW:0,DRAW_MASK);
  12. }

從這個方法的註釋,我們可以看出,如果你想重寫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。


相關文章