ViewGroup爲什麼不會調用onDraw

轉自 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的值的計算代碼如下:

[java] view plaincopy
  1. final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && 
  2.                 (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
[java] view plaincopy
  1. final boolean dirtyOpaque = (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,我們看一看它的實現: 

[java] view plaincopy
  1. /**
  2.   * If this view doesn't do any drawing on its own, set this flag to
  3.   * allow further optimizations. By default, this flag is not set on
  4.   * View, but could be set on some View subclasses such as ViewGroup.
  5.   *
  6.   * Typically, if you override {@link #onDraw} you should clear this flag.
  7.   *
  8.   * @param willNotDraw whether or not this View draw on its own
  9.   */ 
  10. public void setWillNotDraw(boolean willNotDraw) { 
  11.      setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); 
  12.  
[java] view plaincopy
  1. /** 
  2.   * If this view doesn't do any drawing on its own, set this flag to 
  3.   * allow further optimizations. By default, this flag is not set on 
  4.   * View, but could be set on some View subclasses such as ViewGroup. 
  5.   * 
  6.   * Typically, if you override {@link #onDraw} you should clear this flag. 
  7.   * 
  8.   * @param willNotDraw whether or not this View draw on its own 
  9.   */  
  10.  public void setWillNotDraw(boolean willNotDraw) {  
  11.      setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);  
  12.   
  13.  }  

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章