Android 自定義View之View的繪製流程(一)
View繪製流程網上已經有很多詳細介紹的文章,這裡權當給自己最近在這方面的學習的一個記錄,另外本文主要是記錄自己在實際程式碼的一些寫法,一來記錄基礎知識方便自己後面複習二來在開發中可以參考這段時間的程式碼記錄多作參考,一般遇到問題都會在網上找答案,如果自己多記錄參考自己的文章何樂而不為呢。
1. 從setContentView(int layoutId)方法說起
做Android開發的都知道,我們寫的XML佈局檔案是通過在Activity的onCreate()方法的setContentView()方法載入到螢幕上的,在XML檔案中包含了ViewGroup(LinearLayout, FrameLayout)和View(TextView, Button, ImageView),那麼它們是怎麼顯示上去的呢?
先看圖
從圖中可以看出其實setContentView並沒有View或者佈局檔案做顯示的動作,只是做了一些初始化的工作,主要包括以下幾點:
1. 獲取Windows物件,Window是一個抽象類,在Android實現中目前只有一個實現類,PhoneWindow,在我們的Activity中呼叫setContentView其實呼叫的PhoneWindow物件的setContentView方法。
2. 在PhoneWindow中的setContentView中呼叫了installDecor方法,在installDecor方法中呼叫了generateDecor方法,然後new DecorView,建立 了一個DecorView物件,這個DecorView物件其實是PhoneWindow的一個內部類,相當於是一個View的包裝類,除了View引用之外還包含有其它一些屬性,如以Title Bar的顯示管理。
3. 呼叫LayoutInflater的inflate方法初始化一個系統的XML佈局檔案如R.layout.screen_simple,這個系統的佈局檔案就是我們螢幕上看到的顯示的全部,包括TitleBar,ActionBar及mContentParent等內容,其中mContentParent是用來顯示載入我們自己的佈局XML的一個ViewGroup,其實是一個FrameLayout,然後把加載出來的這個佈局檔案對應的View賦值給mDecor,到這兒,mDecor就初始化完成了,而且在這裡有兩個重要物件也被初始化了,即mDecor和mContentParent物件,平時大家在Codding過程中想必會經常直接或者間接用到這兩個物件吧。
4. 再呼叫LayoutInflater的inflate方法解析我們自己的XML佈局檔案,也就是我們在自己的Activity中onCreate方法中呼叫setContentView方法中傳入的Layout ID,然後把解析出來的View新增到步驟2中的物件mContentParent中,這樣就把我們自己的Layout新增到mDecor中。
看到這裡也只是把我們自己的Layout新增到了mDecor中但是還沒有真正的顯示到螢幕上,下面我們就接著繼續。
2. 控制元件是怎麼顯示的
相信大家都對Activiy的生命週期都知道吧,前面我們在生命週期中的onCreate方法中呼叫了setContentView方法,在Activity生命週期方法中緊接著就是onResume方法,在Activity的呼叫流程中我們知道,它都是通過Binder程序間通訊來實現的,通過ActivityManagerService來具體實現的,而最終是通過Handler訊息分發機制最張呼叫到ActivityThread中mH的handleMessage中what等於RESUME_ACTIVITY的條件中,最終呼叫了ActivityThread中的handleResumeActivity方法來進行後面的邏緝的,那麼我們從handleResumeActivity這個方法看起。
套路,還是先上圖:
從前面描述及流程圖中可以看出,View的測量(Measure),佈局(Layout),繪製(Draw)都是在生命週期的onResume在進行的,但其實最終還是呼叫View自身或者其父容器完成的,具體過程如下:
1. 在onCreate方法中也即Activity生命週期的第一個方法我們通常會呼叫setContentView(layoutID),這個過程我們在前面已經說過了,緊接著進行生命週期的第二個方法onResume(通過AMS及Handler機制),最終會通過mH中handleMessage()方法呼叫what=RESUME_ACTIVITY的邏輯判斷體內,即呼叫handleResumeActivity方法。
2. 在handleResumeActivity方法中會呼叫WindowManagerImpl的addView(mDecor,LayoutParam)把前面建立的DecorView(即從系統載入的那個XML佈局檔案,同樣也包含了我們自己的佈局檔案)。
3. 在WindowManagerImpl內部其實是通過WindowManagerGlobal去完成addView的操作的。
4. 在WindowManagerGlobal的addView方法中呼叫了ViewRootImpl的setView方法,在這個方法中依次呼叫requestLayout、scheduleTraversals然後通過Choreographer呼叫到doTraversal方法中,這裡Choreographer並沒有在流程圖中體現出來,這個類我們可以大致理解為其跟Handler,Looper功能類似,用於訊息處理及回撥的,但這裡僅能說類似肯定有很大的不同要不然Google肯定就直接用Handler,至於它的具體實現這裡沒有去特別關注,只需要明白一點,通過它最終會調到doTraversal方法,程式碼如下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
5. 通過doTraversal方法會呼叫到performTraversals方法中,在這個方法中首先會設定一個Rect為mWinFrame表示可以繪製大區域,然後呼叫方法performMeasure開始所有View的測量工作。
6. 在performMeasure內部最終於會通過呼叫onMeasure(widthMeasureSpec, heightMeasureSpec)方法,這個方法我想應該相當熟悉了,我們在自定義View過程中就是通過這個方法來實現我們自己的業務需求測量過程的。
7. 完成測量工作後緊接著進入performLayout方法中,在這個方法中進行擺放工作,通過layout方法最終會呼叫到View的onLayout(changed, l, t, r, b)方法中,這個方法想必也應該很親切吧,我們在自定義View的過程中就是通過這個方法來實現View的佈局工作的。
8. 完成Layout後會進入performDraw方法中,然後呼叫draw方法,drawSoftware方法,然後呼叫到View.draw(canvas),在draw方法中,依次進行background、view's content、Draw children、Draw decorations (scrollbars for instance)過程,其中view's content是我們自定義View的顯示內容,其實就是呼叫onDraw(Canvas)方法來實現繪製的,關於onDraw方法這裡也不再多提,它就是通過Canvas及Paint在指定的區域繪製我們想要的內容。
這裡我們就完成了View的3個主要步驟,Measure、Layout、Draw,注意在這3個過程中,其都是採用遞迴的方式從上到下依次進行測量、擺放和繪製過程的。
3. 小結
根據以上1、2小節已經對View的繪製流程在Android中有了一個大致的認識,對於Android原始碼分析只需要對流程和一些關鍵點掌握即可,不必要每個點每行程式碼都必須分析得很清楚,有這些時間我們可以用來做些其它更有用的積累,對流程瞭解後對於在程式碼編寫和遇到一些莫名其妙的Bug上會有很在幫助,在遇到一些問題時可以通過檢視原始碼幫助解決,好了,總結一下View的繪製流程:
1. 在onCreate 方法中通過setContentView將View及ViewGroup初始化,即將我們自己的XML檔案轉換了Java物件,並新增到系統根佈局DecorView中。
2. 在onResume方法中先將我們的DecorView新增到WindowManager然後通過ViewRootImpl來進行View的具體繪製流程。
3. 在ViewRootImpl的performTraversals()中呼叫performMeasure然後分別呼叫View的onMeasure方法對View自己進行測量,測量即計算自己的weight和height。這個過程完成後,我們就可以通過getMeasuredWeight和getMeasuredHeght方法來獲取View測量的寬高。
4. 然後通過performLayout最終通過View自己的onLayout方法來擺放自己,即把自己放到合適的位置,在這個過程中我們應該注意View的邊距問題。
5. 最後通過performDraw進行具體的繪製,這個方法最張也是呼叫View自己的onDraw方法進行繪製的,一般在ViewGroup中會呼叫DrawChild方法進行子View的繪製。
給出一個關係包含圖,方便記憶:
View的繪製就簡單說到這裡,後面將會有專門的文章來介紹前面提到的View的Measure測量、Layout擺放及Draw繪製的相關知識及簡單實踐期待自己有更多的時間學習。
相關推薦
Android自定義控制元件view(草稿版)
Ⅰ、繼承現有控制元件,對其控制元件的功能進行拓展。(拓展功能) Ⅱ、將現有控制元件進行組合,實現功能更加強大控制元件。(佈局重用) Ⅲ、重寫View實現全新的控制元件(不規則效果控制元件) 本文來討論最難的一種自定義控制元件形式,重寫View來實現全新的控制元件。 1.構
androidの自定義控制元件View在Activity中使用findByViewId得到結果為null,解決方法。。
androidの自定義控制元件View在Activity中使用findByViewId得到結果為null 1. 大家常常自定義view,,然後在xml 中新增該view 元件。。如果在Activity 中使用findByViewId 方法獲取該view 時候,返回物件總為
android自定義水平虛線View
import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.DashPathEffe
android 自定義實現滾動View:WheelView
專案中用到一個比較覺得不錯的控制元件:WheelView,即上下滾動View。它是繼承ScrollView實現,在Android各版本上的效果都是如下:也許在git上有許多這樣功能的控制元件,但個人認為這個控制元件實現的方式簡單,比較讓人容易理解,對自定義控制元件的實現有借
android 自定義ViewGroup之浪漫求婚
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 1、最終效果 有木有發現還是很小清新的感覺 2、看整體效果這是一個scrollView,滑動時每個子view都有一個或多個動畫效果,但是如果我們直接給每個子view加上動畫去實現這個需求
Android自定義元件之日曆控制元件-精美日曆實現(內容、樣式可擴充套件)
需求 我們知道,Android系統本身有自帶的日曆控制元件,網路上也有很多開源的日曆控制元件資源,但是這些日曆控制元件往往樣式較單一,API較多,不易於在實際專案中擴充套件並實現出符合具體樣式風格的,內容可定製的效果。本文通過自定義日曆控制元件,實現了在內容和樣
Android自定義元件之美化radiobutton
其實美化方法很簡單隻需重寫ondraw方法是替換圖片即可 下面是原始碼 package com.myradio; import android.content.Context; import android.graphics.Canvas; import an
Android 自定義標籤選擇按鈕組(一)
效果圖: 一、原理: 1.其實這裡我們用到的是一個ViewGroup控制元件組,把這些按鈕加進去就有這種效果了!不過這裡要繼承ViewGroup(命名為:GoodsViewGroup)重寫裡面的一些方法。 2.主要的方法有: GoodsViewGroup按鈕組
Android自定義控制元件系列案例【一】
Android自定義控制元件的重要性就不多說了,總之是技術進階,面試常見,高薪必備。 本篇博文的目的很簡單,就是希望通過自定義控制元件來解決一個常見需求點,從而感受一下自定義控制元件的魅力與強大。 案
Android 自定義View之View的繪製流程(一)
View繪製流程網上已經有很多詳細介紹的文章,這裡權當給自己最近在這方面的學習的一個記錄,另外本文主要是記錄自己在實際程式碼的一些寫法,一來記錄基礎知識方便自己後面複習二來在開發中可以參考這段時間的程式碼記錄多作參考,一般遇到問題都會在網上找答案,如果自己多記錄參考自己的文章
Android進階——自定義View之View的繪製流程及實現onMeasure完全攻略
引言 Android實際專案開發中,自定義View不可或缺,而作為自定義View的一種重要實現方式——繼承View重繪尤其重要,前面很多文章基本總結了繼承View的基本流程:自定義屬性和繼承View重寫onDraw方法——>實現構造方法並完成相關初始化操
Android自定義View之使用Path繪製手勢軌跡和水波效果
先看下效果圖: 繪製軌跡 繪製手指的軌跡主要是攔截View的onTouchEvent()方法,並根據手指的軌跡繪製path。path中有兩種可以實現的方法 1、Path.lineTo(x,y)方法 public class MoveP
【朝花夕拾】Android自定義View篇之(四)Canvas繪製文字教程
前言 前面的文章中在介紹Canvas的時候,提到過後續單獨講Canvas繪製文字,因為這一節內容比較細緻,內容很多。這裡先宣告一下,本文的內容的來源於騰訊課堂中“仍物線學堂”中課件,因為該課件對常用的繪製文字基本技巧做了比較詳細的講解
Android自定義View之分貝儀
一、說明 最近在整理自定義View方面的知識,偶爾看到meizu工具箱的分貝儀效果,感覺它的實效效果還挺好的,遂想自己模擬實現練練手,廢話不多說,直接開擼。 二、效果圖 首先看一下效果圖: 看效果還挺炫酷
Android自定義View——canvas 繪製一個會動的時鐘
文章目錄 ####1、功能例項 用canvas 繪製一個 會動的 指標式 時鐘 ####2、程式碼架構 ####3、主要功能程式碼 activity_main.xml 檔案 <?xml version="1.0" encodin
Android自定義View之Canvas
https://www.jianshu.com/p/fb18c28d6627 用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得咱自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們來了解下Canvas。 Canvas Canvas我們可
Android 自定義View例項之進度圓環
自定義View的相關文章: Android 實現一個簡單的自定義View Android 自定義View步驟 Android Paint詳解 Android 自定義View之Canvas相關方法說明 Android 自定義View例項之 “京東跑”
Android 自定義View例項之 “京東跑”
自定義View的相關文章: Android 實現一個簡單的自定義View Android 自定義View步驟 Android Paint詳解 Android 自定義View之Canvas相關方法說明 Android 自定義View例項之 “京東跑”
Android 自定義View之Canvas詳解
自定義View的相關文章: Android 實現一個簡單的自定義View Android 自定義View步驟 Android Paint詳解 Android 自定義View之Canvas相關方法說明 Android 自定義View例項之 “京東跑”
【Android 自定義View】之PermuteView
1.前言 最近在專案迭代時,遇到新的UI需求,如下: 看到之後我分析了一下有那些實現方式: 1.使用第三款庫分別實現上下部分的UI功能。 2.讓UI做圖片,同background+press實現。 3.自定義View實現。 第 1