Android View的繪製過程
一切的起源
之前有分析過Activity的啟動過程,view的繪製起源其實也是包含在其中的,老規矩,先上圖:
首先,DecorView是Activity的根view,Activity#setContentView其實就是在DecorView中加入子view,從圖中可知,DecorView的繪製起點是在Activity的建立過程中(onResume之前)觸發的,由ViewRootImpl承擔DecorView的繪製過程,DecorView作為ViewGroup會最終呼叫子view的繪製,繪製過程主要分為三個部分:measure(測量)、layout(佈局)、draw(繪製)。
measure(測量)過程
其實View的繪製過程和事件傳遞過程都是類似的,從最底層的DecorView開始,一層層向上冒泡傳遞,如圖所示:
View的measure過程算是繪製過程中最複雜的一部分了,相比於layout、draw只需要拿到測量後的尺寸直接應用即可,measure過程一開始並不知道ViewGroup的最終大小,需要遍歷測量所有子view的尺寸,再進行自身的測量,如果有問題可能還需進行再次測量才能得到最終的尺寸,一次繪製過程可能需要多次measure才能完成,所以在自定義view時,最好不要在measure過程去獲取view的最終尺寸,在layout之後獲取才比較可靠。
measure的尺寸資訊使用MeasureSpec來儲存,包含mode、size兩部分的資訊,關於mode有三種模式:
MeasureSpec.UNSPECIFIED:不限尺寸,一般只有系統中的view才能用到
MeasureSpec.AT_MOST:指定最大可用大小,對應於wrap_content
MeasureSpec.EXACLITY:固定大小,對應於match_parent和指定具體寬高
自定義在支援wrap_content時,需要注意複寫onMeasure方法,原始碼wrap_content會直接當作match_parent處理:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST: //對應的wrap_content直接取specSize,相當於match_parent
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
layout和draw過程
layout過程很簡單,只需要遍歷子view根據規則排列即可。draw過程我們最熟悉的應該是onDraw方法,大部分情況下自定義只需要複寫該方法即可,但在對於某些特殊顯示效果時就需要注意各個draw方法之間的前後順序了,後面呼叫的方法繪製的內容總是會覆蓋在前面。
AdjustLayout自動換行佈局
這邊寫了一個自動換行的AdjustLayout自定義view,大家有興趣可以參考手動去重寫繪製過程,相信會對整個繪製流程有更深刻的印象。github地址:AdjustLayout
思考題
文章最初分析了view的繪製起源,介於Activity的onCreate和onResume之間,請大家思考下在onResume中能否獲取到view的measuredWidth?這個思考題主要涉及到ViewRootImpl.performTraversal的呼叫方式和Android的訊息機制,類似的問題可以換成在onCreate方法中呼叫View.post(),是post中的方法先執行還是onResume先執行到,有興趣的童鞋可以嘗試下。(其實這個問題再深入挖掘可以關聯到ipc的執行機制,是否會阻塞呼叫執行緒呢?)
相關文章
- 初探Android的View繪製過程AndroidView
- View 的繪製過程View
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- Android繪製View的過程研究——計算View的大小AndroidView
- 探究Android View 繪製流程,Xml 檔案到 View 物件的轉換過程AndroidViewXML物件
- Android View繪製原理:繪製流程排程、測算等AndroidView
- Android View繪製流程AndroidView
- View的繪製二:View的繪製流程View
- 初·Android View的繪製流程AndroidView
- 深入淺出Android系列之從ViewToBitmap延伸到View的繪製全過程AndroidView
- Android 中 View 繪製流程分析AndroidView
- View繪製01-Android渲染系統中的ViewViewAndroid
- 【Android原始碼】View的繪製流程分析Android原始碼View
- 你需要知道的 Android View 的繪製AndroidView
- Android進階(五)View繪製流程AndroidView
- Android View 繪製流程(Draw) 完全解析AndroidView
- Android View繪製13問13答AndroidView
- 流程圖繪製的操作過程流程圖
- 探究Android View 繪製流程,Canvas 的由來AndroidViewCanvas
- 深入理解 Android 之 View 的繪製流程AndroidView
- 探究 Android View 繪製流程,Activity 的 View 如何展示到螢幕AndroidView
- Android原始碼分析之View繪製流程Android原始碼View
- Android View繪製原始碼分析 MeasureAndroidView原始碼
- View 繪製體系知識梳理(4) 繪製過程之 Layout 詳解View
- View 繪製體系知識梳理(5) 繪製過程之 Draw 詳解View
- View繪製——畫多大?View
- View繪製——畫在哪?View
- View 繪製流程分析View
- View繪製(一) performTraversalsViewORM
- android繪製花邊view-WaveLineViewAndroidView
- View的繪製三:UI繪製的三大步驟ViewUI
- View繪製——怎麼畫?View
- Flutter 自定義繪製 ViewFlutterView
- Android View繪製流程看這篇就夠了AndroidView
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Android自定義View之Paint繪製文字和線AndroidViewAI
- 遊戲原畫教程:手部繪製過程遊戲
- View 繪製體系知識梳理(6) 繪製過程之 requestLayout 和 invalidate 詳解View