天天看點

How Android Draws Views(Android 視圖繪制機制)

當一個Activity得到焦點以後,它就會開始繪制自己的布局。繪制的過程由Android架構來管理,但是這個Activity必須提供自己布局的根節點。

繪制過程從布局的根節點開始,測量、繪制布局。繪制過程将會周遊整個視圖樹并處理每一個view(原文直接翻譯是處理每一個與無效區域交叉的view)。按照順序,每一個ViewGroup都有責任要求他們的子view被繪制(通過onDraw()方法),并且每一個子view都有責任繪制自己。因為視圖樹是按順序解析的,這就意味着父View會比子View先繪制,同一層的View會按照他們在視圖樹中出現的順序依次繪制。

繪制過程分兩步:測量過程和擺放過程。測量過程在measure(int,int)方法中實作,并且測量過程會從頭到尾貫穿整個View樹的繪制過程。每一個View以遞歸的形式把自己的尺寸規格放在View樹下。在測量步驟的最後,每一個視圖都已經記錄了自己的尺寸。第二個步驟發生在layout(int,int,int,int)方法中,它也是從頭到尾貫穿的。在這個步驟中每一個父view會按照測量步驟中計算的尺寸來擺放他們的子View。

Android 架構不會繪制不在有效區域的View(原文直接翻譯是Android不會繪制不在無效區域的View,也就是說Android隻會繪制無效區域的視圖,我覺得他可能寫錯了),同樣也不會關注視圖的背景。

你可以調用一個View的invalidate()方法來強制繪制這個視圖

當一個View對象的measure()方法傳回時,它的getMeasuredWidth()和getMeasuredHeight()值必須已經被設定了,包括所有它的子View。一個View對象的測量寬度和測量高度會受到這個View父視圖的限制。這保證了在測量步驟的最後所有的父View能夠裝得下他們的子View。父View可能會調用子View的measure()方法不止一次,比如說,父View可能會根據所有的子View指定的大小來判斷它們想占多大的空間,如果所有子view的不受限制大小加起來太大或者太小的話,父View就會調用它們的measure()方法,并傳遞的一個尺寸值(這就是說:如果子View對配置設定給他們的空間不滿意的話,父View就會出面然後在第二步的時候解決這個問題)。

測量步驟使用兩個類來表示尺寸。ViewGroup.LayoutParams 類被子View使用來告訴他們的父View子View想要的大小和位置。基礎的ViewGroup.LayoutParams 類隻是描述了子View的寬和高。對于每一個尺寸而言,他可以是下面的值之一:

  • 一個明确的數值
  • MATCH_PARENT,子View想要跟父View一樣大
  • WRAP_CONTENT, 子View想要能夠包含自己的内容就可以了。

ViewGroup.LayoutParams 有一些子類可以被ViewGroup的子類使用。比如,RelativeLayout有自己的ViewGroup.LayoutParams的子類,包括了可以使子View水準或垂直居中的屬性。

MeasureSpec 類用來把測量請求從父View向子View傳遞。一個MeasureSpec 可以是下面的一種模式:

  • UNSPECIFIED:父View使用這個參數來判斷子View想要的尺寸。舉個例子:一個LinearLayout可能會調用子View的measure()方法,并且設定height為UNSPECIFIED width 為240 來看一下子View在寬度為240 像素時會是什麼樣子。
  • EXACTLY:父View使用這種模式來給子View強制制定一個明确的大小。子View必須使用這個尺寸,并且保證它的所有子類都在這個尺寸的範圍内。
  • AT MOST: 父View使用這種模式給子View制定一個最大的尺寸。子View必須保證它自己和它所有的子類都滿足這個大小。

如果想要初始化一個layout,可以調用requestLayout()方法。這個方法通常被view自己調用,當它覺得它的大小不适應現在的邊界的時候。