為什么自定義ViewGroup ondraw方法不會被調用

CarrieN40 8年前發布 | 12K 次閱讀 Android開發 移動開發

viewgroup的繪制嚴格來說是在dispatchDraw中完成,作為一個容器,繪制自己的孩子該通過dispatchDraw(canvas)實現。但是你非要在ondraw中也不會出錯。

但自定義ViewGroup ondraw方法不會被調用的根本原因是你的ViewGroup中還沒有可繪制的內容。

下面是詳細解釋:

一,現象

<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);

當View初始他時,它會調用一個私有方法:computeOpaqueFlags,這里面列出了不透明的三個條件:

       // 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); }</pre>

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

     

 本文由用戶 CarrieN40 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!