Android 如何繪製檢視畫面

Activity 收到焦點時,系統會要求繪製版面配置。Android 架構會處理繪圖的程序,但 Activity 必須提供其版面配置階層的根節點。

繪圖會從版面配置的根節點開始。要求測量並繪製版面配置樹狀結構。系統會追蹤樹狀結構並算繪每個與無效區域相交的 View,藉此處理繪圖。因此,每個 ViewGroup 會負責要求繪製其各個子項 (使用 draw() 方法)。每個 View 會負責繪圖本身。由於樹狀結構預先進行遍歷,這意味著父項將被繪製其子項之前 (即之後),同層級則按照其在樹狀結構中出現的順序繪製。

注意:此架構不會繪製不在有效區域內的 View 物件,並且會為您繪製 View 背景。

您可以呼叫 invalidate() 來強制 View 進行繪圖。

繪製版面配置分為兩種程序:測量過程和版面配置過程。

測量過程

測量票證已實作 measure(int, int) 中,且位於 View 樹狀結構的上一層周遊。各個 View 會在遞迴期間將維度規格推送到樹狀結構中。測量過程結束時,每個 View 都會儲存測量結果。第二個過程是在 layout(int, int, int, int) 中進行,且為由上而下。在票證中,每個父項都必須使用測量票證計算的大小來指定所有子項。

View 物件的 measure() 方法回傳,getMeasuredWidth()getMeasuredHeight() 值必須設定,以及所有 View 物件的子系。View 物件的測量寬度和測量高度值必須符合 View 物件父項所設的限制。此做法可確保在評估標準結束時,所有父項都必須接受子項的所有測量。父項 View 可以對其子項多次呼叫 measure()。舉例來說,父項可以用未指定的維度測量每個子項一次,藉此判斷所需的維度,如果所有子項的未限制大小總和過大或過小 (亦即各個子項彼此不認同各自獲得的空間,父項就會攔截並在第二次過程時設定規則),則再次用實際數字對其呼叫 measure()

測量過程會透過兩個類別來傳送維度。View 物件會使用 ViewGroup.LayoutParams 類別,向父項說明評估方式和位置。基本 ViewGroup.LayoutParams 類別僅會說明 View 想要的寬度和高度的範圍。每個維度都可以指定下列其中一項:

  • 實際數字
  • MATCH_PARENT,這表示 View 想要與父項一樣大 (減去邊框間距)
  • WRAP_CONTENT,這表示 View 的大小必須夠高,才能納入其內容 (加上邊框間距)。

ViewGroup 的不同子類別有 ViewGroup.LayoutParams 的子類別。舉例來說,RelativeLayout 有專屬的 ViewGroup.LayoutParams 子類別,能夠水平和垂直將子項 View 物件置中。

MeasureSpec 物件的用途是將樹狀結構從父項向下推到子項。MeasureSpec 可為以下三種模式之一:

  • UNSPECIFIED:父項會使用這個屬性來確定子項 View 的所需維度。舉例來說,LinearLayout 可以在其子項上呼叫 measure(),高度設為 UNSPECIFIED,寬度為 EXACTLY 240,以找出子項 View 想要獲得 240 像素寬度的高度。
  • EXACTLY:父項會使用這個欄位為子項施加確切大小。子項必須使用這個尺寸,並保證所有子系都符合該尺寸規定。
  • AT MOST:父項會使用這個欄位為子項設定尺寸上限。子項必須保證其及其所有子系在這個尺寸內。

版面配置過程

如要啟動版面配置,請呼叫 requestLayout()。當 View 認為其不再超出目前邊界時,就會自行呼叫這個方法。