什麼是 View ?佈局
View 是 Android 中全部控件的基類,View 能夠是單個控件,也能夠是由多個控件組成的一組控件。ViewGroup 裏面能夠有子 View,子 View 裏面也能夠有 ViewGroup。優化
什麼是 ViewRoot、DecorView ?spa
View 有三大流程,measure、layout、draw,瞭解並熟悉其三大流程對於咱們進行 Android 開發有着極其重要的做用。在熟悉三大流程以前,先介紹一下 ViewRoot 和 DecorView 的概念。orm
Activity 內部 是組合了一個 Window 對象,Activity 全部的事件都是交給 Window 來處理的,可是 Window 是一個抽象類,處理事務具體的操做就只能交給它的實現類,也就是 PhoneWindow,PhoneWindow 就會把事件傳給這個 DecorWindow,這個 DecorWinow 是應用窗口的根容器,也就是個頂級 View,實際上是個 FrameLayout。這個根容器通常狀況下都只有惟一一個子 View ,就是一個垂直的 LinearLayout,上下兩部分分別是標題欄和內容欄。而 ViewRoot 是 View 樹的管理者,它的成員變量 mView 對應的就是一個佈局的根 View,ViewRoot 是 View 和 WindowManger 的橋樑,也就是 DecorView 和 WindowManger 的紐帶,對應的是 ViewRootImpl 類。當 Activity 建立完成後,就會建立 ViewRootImpl 對象,將 DecorView 添加到 Window 中,並將 DecorView 和 ViewRootImpl 創建關聯。對象
View 的三大流程就是從 ViewRoot 的 performTraversals 方法開始的。該方法會依次調用 performMeasure、performLayout、performDraw 三個方法,來完成頂級 View 的三大流程。三個方法又會分別調用 onMeasure、onLayout、onDraw 方法來完成對子 View 的 measure、layout、draw。接着子元素會重複父容器的 measure、layout、draw。來完成整個 View 樹的三大流程。measure 決定 View 的 寬高、layout 肯定四個頂點的位置、draw 則會將內容呈如今屏幕上。繼承
什麼是 MeasureSpec ?事件
爲了更好的瞭解 View 的測量過程,咱們還須要瞭解 MeasureSpec。MeasureSpec 在很大程度上決定了一個 View 的尺寸規格。若是對於 DecorView,其 View 的 MeasureSpec 就是由窗口的大小和自身的LayoutParams 所決定的,可是若是是一個子 View,其 MeasureSpec 則是由父容器的 MeasureSpec 和自身的 LayoutParams 所共同決定的。事務
MeasureSpec 是一個32位 int 值,高兩位表明的是測量模式 SpecMode,低30位表明的是某種測量模式下的規格大小 SpecSize。開發
SpecMode 有三類:UNSPECIFIED、EXACTLY、AT_MOST。get
UNSPECIFIED:父容器不對子 View 作限制,想要多大就多大。
EXACTLY:父容器檢測出 View 的精確大小,這個時候 View 的最終大小就是 SpecSize 所指定的值。對應的是 LayoutParams 中的 match_parent 或 具體數值。
AT_MOST:父容器指定一個大小 SpecSize,子 View 的大小按照自身要求決定,可是不能超過 SpecSize,對應 LayoutParams 中的 wrap_content。
View 的三大流程
measure 過程:
measure 過程要分清狀況,當要測量的只是一個原始的 View 時,那麼只經過 measure 就能夠完成其測量過程,可是若是是一個 ViewGroup,就須要遍歷測量全部的子 View。
下面是 View 的 onMeasure 方法:
它調用了 setMaasureDimension() 方法來設置 View 寬高的測量值,那麼 getDefaSize() 方法又是幹嗎的呢?
它是經過測量模式來返回測量得出的值。當測量模式爲 UNSPECIFIED 時,測量值就爲 result,也就是 getSuggestedMinimumWidth() 或者 getSuggestedMinimumHeight() 返回的值。當測量模式爲 AT_MOST 和 EXACTLY 時,返回的大小就是 MeasureSpec 中測量獲得的 SpecSize。
那麼 getSuggestedMinimumWidth() 和 getSuggestedMinimumHeight() 方法又在幹什麼呢?
這兩個方法會判斷當前 View 的背景是否是爲空,爲空就返回 mMinWidth/mMinHeight(即爲 minWidth 和 minHeight 屬性設置的值),不爲空就返回 mMinWidth/mMinHeight 和 背景的 getMinimumWidth()/getMinimumHeight() 返回值中的較大的那個值。
而 ViewGroup 的 measure 過程以下:
對於 ViewGroup 來講,不只須要測量自身,還須要測量全部的子 View。和 View 不一樣的是,ViewGroup 是一個抽象類,因此沒有重寫 View 的 onMeasure() 方法,而是提供了 measureChildren() 的方法。
該方法會遍歷 ViewGroup 的每個 Child,而後調用 measureChild() 方法。
該方法會獲得子元素的 LayoutParams,再經過 getChildMeasureSpec 來獲取子元素的 MeasureSpec,而後調用 View 的 measure() 方法進行測量。
layout 過程:
layout 過程是爲了肯定 View 的位置,在 ViewGroup 位置被肯定後,它會遍歷並肯定全部子元素的位置。
下面是 View 的 layout() 方法:
layout() 方法首先會經過 setFrame() 方法來設定四個頂點的位置,四個頂點一旦肯定,View 在父容器中的位置也就肯定了,接着會調用 onLayout() 方法。這個方法是父容器肯定子元素的位置,由於佈局選擇的不一樣,確認的方式也會不同,因此在 View 和 ViewGroup 中都沒有真正實現 onLayout()。具體的 onLayout() 實現要看選擇的佈局方式。
ViewGroup 中,會使用 onLayout() 方法遍歷全部子元素並調用其 layout() 方法,在 layout() 方法中又會調用 onLayout() 方法,直到肯定完整個 View 樹的位置。
draw 過程:
draw 過程是將 View 繪製到屏幕上,View 源碼中的 draw() 方法比較長,就不復制到這了,感興趣的小夥伴能夠本身去看看。
draw() 方法主要有四個部分:繪製背景、繪製本身、繪製 children、繪製裝飾。
繪製過程的傳遞是經過 dispatchDraw() 方法來實現的,會遍歷全部子元素的 draw() 方法,一層一層繪製完整個 View 樹。
View 還有一個特殊方法,setWillNotDraw(),以下:
表示若是 View 不須要繪製任何東西時,會將 flags 設置爲 true,而後進行相應的優化。默認狀況下,View 沒有啓用這個優化標誌位,可是 ViewGroup 默認啓用了。當一個自定義 View 繼承自 ViewGroup 而且自己不須要繪製時,能夠開啓這個標誌位方便後續優化,若是須要繪製,就得顯性關閉 WILL_NOT_DRAW 這個標誌位。