Carson帶你學Android:一文梳理自定義View工作流程


前言

  • 自定義ViewAndroid 開發者必須了解的基礎
  • 網上有大量關于自定義View 原理的文章,但存在一些問題:內容不全、思路不清晰、無源碼分析、簡單問題復雜化等等
  • 今天,我將全面總結自定義View 的原理,我能保證這是市面上的最全面、最清晰、最易懂的
  1. 本文秉著“結論先行、詳細分析在后”的原則,即先讓大家感性認識,再通過理性分析從而理解問題;
  2. 所以,請各位讀者先記住結論,再往下繼續看分析;
  3. 文章較長,閱讀需要較長時間,建議收藏等充足時間再進行閱讀

Carson帶你學Android自定義View文章系列:
Carson帶你學Android:自定義View基礎
Carson帶你學Android:一文梳理自定義View工作流程
Carson帶你學Android:自定義View繪制準備-DecorView創建
Carson帶你學Android:自定義View Measure過程
Carson帶你學Android:自定義View Layout過程
Carson帶你學Android:自定義View Draw過程
Carson帶你學Android:手把手教你寫一個完整的自定義View
Carson帶你學Android:Canvas類全面解析
Carson帶你學Android:Path類全面解析


目錄

示意圖

1. 儲備知識

1.1 ViewRoot

  • 定義
    連接器,對應于ViewRootImpl

  • 作用

    1. 連接WindowManagerDecorView
    2. 完成View的三大流程: measurelayoutdraw
  • 特別注意

// 在主線程中,Activity對象被創建后:
// 1. 自動將DecorView添加到Window中 & 創建ViewRootImpll對象
root = new ViewRootImpl(view.getContent(),display);

// 3. 將ViewRootImpll對象與DecorView建立關聯
root.setView(view,wparams,panelParentView)

1.2 DecorView

  • 定義:頂層View

Android 視圖樹的根節點;同時也是 FrameLayout 的子類

  • 作用:顯示 & 加載布局

View層的事件都先經過DecorView,再傳遞到View

  • 特別說明
    內含1個豎直方向的LinearLayout,分為2部分:上 = 標題欄(titlebar)、下 = 內容欄(content)
示意圖

Activity中通過 setContentView()所設置的布局文件其實是被加到內容欄之中的,成為其唯一子View = id為content的FrameLayout

// 在代碼中可通過content得到對應加載的布局

// 1. 得到content
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
// 2. 得到設置的View
ViewGroup rootView = (ViewGroup) content.getChildAt(0);

1.3 Window、Activity、DecorView 與 ViewRoot的關系

  • 簡介
示意圖

1.4 自定義View基礎

了解自定義View流程前,需了解一定的自定義View基礎,具體請看文章:Carson帶你學Android:自定義View基礎


2. 繪制準備

  • 回憶上圖,可看出最后1步 = 繪制


    示意圖
  • 但在繪制前,系統會有一些繪制準備,即前面幾個步驟:創建PhoneWindow類、DecorView類、ViewRootmpl類等

故,下面我會先將繪制前的準備,再開始講繪制流程


3. 繪制流程概述

  • 從上可知,View的繪制流程開始于:ViewRootImpl對象的performTraversals()
  • 源碼分析
/**
  * 源碼分析:ViewRootImpl.performTraversals()
  */
  private void performTraversals() {

        // 1. 執行measure流程
        // 內部會調用performMeasure()
        measureHierarchy(host, lp, res,desiredWindowWidth, desiredWindowHeight);

        // 2. 執行layout流程
        performLayout(lp, mWidth, mHeight);

        // 3. 執行draw流程
        performDraw();
    }
  • 從上面的performTraversals()可知:View的繪制流程從頂級View(DecorView)ViewGroup開始,一層一層從ViewGroup至子View遍歷測繪

即:自上而下遍歷、由父視圖到子視圖、每一個 ViewGroup 負責測繪它所有的子視圖,而最底層的 View 會負責測繪自身

  • 繪制的流程 = measure過程、layout過程、draw過程,具體如下
示意圖
示意圖

下面,我將詳細講解View繪制的三大流程:measure過程、layout過程、draw過程


4. 詳細介紹

4.1 Measure 過程

  • 作用
    測量View的寬 / 高
  1. 在某些情況下,需要多次測量(measure)才能確定View最終的寬/高;
  2. 該情況下,measure過程后得到的寬 / 高可能不準確;
  3. 此處建議:在layout過程中onLayout()去獲取最終的寬 / 高
  • 具體流程
示意圖
示意圖

4.2 Layout過程

  • 作用
    計算視圖(View)的位置

即計算View的四個頂點位置:LeftTopRightBottom

  • 具體流程


    示意圖
示意圖
  • 詳細講解

請看文章:Carson帶你學Android:自定義View Layout過程

4.3 Draw過程

  • 作用
    繪制View視圖

  • 具體流程

示意圖
示意圖

至此,關于自定義View的工作流程講解完畢。


5. 自定義View的步驟

步驟1:實現Measure、Layout、Draw流程

  • 從View的工作流程(measure過程、layout過程、draw過程)來看,若要實現自定義View,根據自定義View的種類不同(單一View / ViewGroup),需自定義實現不同的方法
  • 主要是:onMeasure()onLayout()onDraw(),具體如下
示意圖

步驟2:自定義屬性

  1. 在values目錄下創建自定義屬性的xml文件
  2. 在自定義View的構造方法中加載自定義XML文件 & 解析屬性值
  3. 在布局文件中使用自定義屬性

6. 實例講解

結合原理 & 實現步驟,若需實現1個自定義View,請看文章:Carson帶你學Android:手把手教你寫一個完整的自定義View


7. 總結


歡迎關注Carson_Ho的簡書

不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度


請點贊!因為你的鼓勵是我寫作的最大動力!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容