Android 從0開始自定義控制元件之 ViewRoot 與 DecorView (五)

Airsaid發表於2016-12-08

轉載請標明出處: http://blog.csdn.net/airsaid/article/details/53525335
本文出自:周遊的部落格

前言

好幾個星期沒寫部落格了,今天繼續來學習 View 相關的知識:View 的三大流程:測量流程、佈局流程、繪製流程。
在瞭解這三大流程之前,首先來了解下 View 的幾個基本概念,關於 ViewRoot 和 DecorView。

ViewRoot

VIewRoot 光看名字,貌似是 ViewTree 的根節點?其實萬萬不是, ViewRoot 或者說對應的 ViewRootImlp 類,它其實是連線 WindowManger 和 DecorView 的橋樑。View 的三大流程都是通過 ViewRoot 來完成的。

在 ActivityThread 中,當 Activity 物件被建立完畢後,會將 DecorView 新增到 Window 中,同時會建立 ViewRootImpl 物件,並將 ViewRootImpl 物件與 DecorView 進行關聯。

整個 View 的繪製流程,是從 ViewRoot 的 performTraversals 方法開始的。
它經過了 measure、layout、draw 這三個過程才最終將一個 View 繪製出來,其中 measure 是用於測量 View 的寬高的,layout 是用於當繼承 ViewGroup 時確定子 View 位置的, draw 則是負責將 View 繪製在螢幕上。

performTraversals 會依次呼叫 performMeasure、performLayout、performDraw 這三個方法,這三個方法會依次完成頂級 View 的 measure、layout、draw 三大流程。

其中 performMeasure 方法會接著呼叫 measure 方法,在 measure 方法中又會去呼叫 onMeasure 方法。在 onMeasure 方法中對所有的子元素進行了 measure 過程 ,這個時候 measure 流程就從父容器傳遞到子元素中了,這樣就完成了一次 measure 過程。接著,子元素又重複了一遍 measure 過程,如此反覆,直到沒有了子元素。這樣就完成了整個 View 樹的遍歷。

其中 layout 和 draw 的流程同上,兩者同樣都有 performLayout 、performDraw 和 layout、draw 方法,唯一不同的是,performDraw 的傳遞是在 draw 方法中通過 dispatchDraw 來實現的,不過這並沒有什麼本質上的區別。

稍作整理一下,其中各流程各決定了 View 的如下方面:

  • measure 過程決定了 View 的寬/高。完成以後,可以通過 getMeasureWidth 和 getMeasureHeight 來獲取 View 測量後的寬/高。

  • layout 過程決定了 View 四個定點的座標和實際 View 的寬/高。完成以後,可以通過 getTop、getLeft、getBottom、getRight 來獲取 View 的四個頂點的位置,並可以通過 getWidth 和 getHeight 方法獲取 View 的最終寬/高。

  • draw 過程決定了 View 的顯示,只有當 draw 方法完成以後,View 才能夠顯示到螢幕上。

DecorView

DecorView 繼承自 FrameLayout,是一個 ViewGroup。在整個 ViewTree 中, DecorView 是整個 ViewTree 的頂層 View。View 的所有事件,都先經過 DecorView,然後再傳遞給 View。

DecorView 在一般情況下,內部會包含一個豎直的 LInearLayout,裡面有上下兩部分,上面是標題欄,下面是內容,內容佈局有一個預設的 id: content。

我們在 Activity 中 通過 setContentView() 方法設定的佈局,其實就是新增到了內容部分裡。如果我們想獲取到內容佈局的話,可以通過如下方法獲取:

ViewGroup content = (ViewGroup) findViewById(android.R.id.content);

想獲取到我們設定的 View 的話,可以通過如下方式獲取:

View childAt = content.getChildAt(0);

參考
《Android 開發藝術探索》

相關文章