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 原始碼解析(三) – View的繪製過程AndroidView原始碼
- View 的繪製過程View
- Android View繪製原理:繪製流程排程、測算等AndroidView
- 深入淺出Android系列之從ViewToBitmap延伸到View的繪製全過程AndroidView
- View的繪製二:View的繪製流程View
- View繪製01-Android渲染系統中的ViewViewAndroid
- Android進階(五)View繪製流程AndroidView
- Android View繪製原始碼分析 MeasureAndroidView原始碼
- 探究Android View 繪製流程,Canvas 的由來AndroidViewCanvas
- 探究 Android View 繪製流程,Activity 的 View 如何展示到螢幕AndroidView
- Android原始碼分析之View繪製流程Android原始碼View
- Android原始碼完全解析——View的Measure過程Android原始碼View
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Android自定義View之Paint繪製文字和線AndroidViewAI
- Android View繪製流程看這篇就夠了AndroidView
- Android自定義view-自繪ViewAndroidView
- View繪製——畫多大?View
- View 繪製流程分析View
- View繪製——畫在哪?View
- Android中View的測量和佈局過程AndroidView
- Android系統原始碼分析--View繪製流程之-inflateAndroid原始碼View
- Android系統原始碼分析–View繪製流程之-setContentViewAndroid原始碼View
- Android系統原始碼分析--View繪製流程之-setContentViewAndroid原始碼View
- View的繪製三:UI繪製的三大步驟ViewUI
- Android高階進階之路【一】Android中View繪製流程淺析AndroidView
- Android自定義View之(一)View繪製流程詳解——向原始碼要答案AndroidView原始碼
- Flutter 自定義繪製 ViewFlutterView
- View繪製——怎麼畫?View
- View的繪製-measure流程詳解View
- Python Matplotlib繪製條形圖的全過程Python
- [Android]多層波紋擴散動畫——自定義View繪製Android動畫View
- View繪製流程原始碼分析View原始碼
- 遊戲原畫教程:手部繪製過程遊戲
- framework——View新增過程FrameworkView
- 每日一問:簡述 View 的繪製流程View
- View的繪製一:View是如何被新增到螢幕視窗上的View
- android View 繪圖雙緩衝技術AndroidView繪圖
- 2018.03.15、View 繪製流程學習 筆記View筆記