在學習View的工作原理之前,需要先學習一些基本的概念。
ViewRoot
Q1: ViewRoot
是什麼?
- 對應
ViewRootImpl
類。- 連線
WindowManagerService
和DecorView
的紐帶。
Q2:ViewRoot
在View繪製中有什麼作用?
- View的三大流程(測量(
measure
),佈局(layout
),繪製(draw
))均通過ViewRoot
來完成。
注意:ViewRoot
並不屬於View樹的一份子。從原始碼實現上來看,它既非View的子類,也非View的父類別,但是,它實現了ViewParent
介面,這讓它可以作為View的名義上的父檢視。
View的繪製流程從ViewRoot
的performTraversals
開始,如圖。
onMeasure
方法會對所有子元素進行measure
過程,在measure
方法中又會呼叫onMeasure
方法,如此反覆最終完成整個View樹的遍歷,layout
,draw
方法同理。
DecorView
Q1:DecorView
是什麼?
包括兩部分,標題列和內容欄,如圖。
DecorView
是FrameLayout
的子類,它可以被認為是Android檢視樹的根節點檢視。setContentView
所佈置的檔案是被加入內容欄的。
Q: DecorView
在View繪製中有什麼作用?
View層的事件都需先經過
DecorView
,再傳遞給View,分發的過程在View體系詳解有講到。
ViewGroup.LayoutParams
Q1:怎麼理解ViewGroup.LayoutParams
?
ViewGroup
的子類(RelativeLayout、LinearLayout)
有其對應的ViewGroup.LayoutParams
子類如:
RelativeLayout
的ViewGroup.LayoutParams
子類 =RelativeLayoutParams
Q2: 這個類有什麼作用?
指定檢視
View
的高度(height)
和 寬度(width)
等佈局引數。
Q3:怎麼使用?
引數 | 解釋 |
---|---|
具體值 | dp / px |
fill_parent | 強制性使子檢視的大小擴充套件至與父檢視大小相等(不含 padding ) |
match_parent | 與fill_parent相同,用於Android 2.3 & 之後版本 |
wrap_content | 自適應大小,強制性地使檢視擴充套件以便顯示其全部內容(含 padding ) |
MeasureSpec
定義:測量規格類。
組成:測量規格
(MeasureSpec)
= 測量模式(mode)
(高2位) + 測量大小(size)
(低30位)。作用:通過寬測量值
widthMeasureSpec
和高測量值heightMeasureSpec
決定View的大小。
Q1:為什麼說是很大程度決定了View的尺寸規格?
答:View的尺寸規格還受父容器影響,因為父容器影響View的MeasureSpec
的建立過程。
Q2:MeasureSpec
有幾種模式?
測量模式
(Mode)
的型別有3種:UNSPECIFIED、EXACTLY 和
AT_MOST。
MeasureSpec
值的計算
View: 取決於View的佈局引數(
LayoutParams
)和父容器的MeasureSpec
值。頂級View: 取決於自身佈局引數 和視窗尺寸。
以下流程圖的方法為原始碼中的方法,感興趣的讀者可以自行檢視原始碼,強推Carson_Ho部落格,內有詳細解讀。
measure
作用:測量View的寬/高。
注意:某些情況需要多次
measure
才能確定View的寬高,此時測試的結果不準確,建議在layout
過程中onLayout()
獲取最終寬/高。
measure
測量有兩種情況:
View
ViewGroup
說明:
Measure
過程中,主要目的就是為了測量出View
的寬/高,在measure()
入口方法中呼叫了onMeasure()
,而在onMeasure
方法中,呼叫了getDefaultSize()
得出測量後View
的寬/高,再呼叫setMeasureDimension()
儲存測量後的寬高。測量過程到此結束,結果是儲存了一個測量後的寬高。
Q1:measure
流程最後使用的是getDefaultSize()
得出的寬/高,那麼這個寬/高是什麼?
AT_MOST和EXACTLY:
getDefaultSize()
返回的大小是measureSpec
中的specSize
,這個specSize
就是最終的測量結果。
UNSPECIFIED:
有背景
寬/高為android:minWidth
屬性所指定的值,若無指定,則為0。
無背景
View
的寬/高度為android:minWidth
/android:minHeight
屬性所指定的值和mBackground.getMinimumWidth()
/mBackground.getMinimumHeight()
中的最大值。
ViewGroup
ViewGroup
除了完成自己的measure過程之外,還會遍歷所有子View的measure方法。
ViewGroup
是一個抽象類,沒有重寫View的onMeasure
方法(自定義View時需要自己實現)。提供了一個
measureChildren
方法。
說明:在
ViewGroup
的measure
過程中,先在入口measure()內呼叫onMeasure()
,與View中的onMeasure
不同,ViewGroup
中沒有實現這個方法,因為不同的ViewGroup
子類(LinearLayout
、RelativeLayout
/ 自定義ViewGroup
子類等)具備不同的佈局特性,這導致他們子View
的測量方法各有不同,故需自己重寫。在這個方法中包含了三個方法
measureChildren()
系統方法,遍歷子View 並且呼叫
measureChild()
進行下一步測量。
measureCarson()
需要自己重寫,合併所有子View的尺寸大小,最終得到
ViewGroup
父檢視的測量值。
setMeasureDimension()
與單一View一樣,儲存測量後的資料。
作用:確定View的位置。
說明:
由於單一View是沒有子View的,故o
nLayout()
是一個空實現。由於在
layout()
中已經對自身View進行了位置計算,所以單一View的layout過程在layout()
後就已完成了。
ViewGroup
說明:
Viewgroup
在onLayout
方法中遍歷了子View,呼叫child.layout()
,計算每個子View的位置,一開始計算ViewGroup
位置時,呼叫的是ViewGroup
的layout()
和onLayout()
,當遍歷子View計運算元View位置時,呼叫的是子View的layout()
和onLayout()
。
作用:將View繪製到螢幕上面。
說明:所有的檢視最終都是呼叫 View 的 draw ()繪製檢視。
ViewGroup
onDraw
方法
onDraw
方法,需要支援wrap_content並且需要自己處理padding。ViewGoup
派生特殊的Layout
(除了系統佈局,重新定義一種新佈局)
ViewGroup
的測量,佈局兩個過程,並同時處理子元素的測量和佈局過程。View
ViewGroup
ViewGroup
的測量和佈局,第二種更接近底層。參考自: