View的繪制是一個(gè)遞歸的過程,父View繪制自己和子View,然后子view繪制自己和自己的子View。
我們知道遞歸的一般數(shù)學(xué)表達(dá)是A1=T,An=f(An-1),那么與之對應(yīng),View繪制的A1和函數(shù)f()又是什么呢?
答案 :View繪制的A1是DecorView,它是View繪制的起始節(jié)點(diǎn),View繪制的f()函數(shù)是measure,layout,draw三大過程,通過遞歸調(diào)用這三個(gè)方法完成View的繪制。
在介紹View的繪制過程之前,先介紹幾點(diǎn)關(guān)于View的相關(guān)概念,方便大家理解。
一、View相關(guān)概念

PhoneWindow:繼承自Window類,負(fù)責(zé)管理界面顯示以及事件響應(yīng),每個(gè)Activity 界面都包含一個(gè)PhoneWindow對象,它是Activity和整個(gè)View系統(tǒng)交互的接口。
DecorView:是PhoneWindow中的起始節(jié)點(diǎn)View,繼承自View類,是作為整個(gè)視圖容器來使用的,主要負(fù)責(zé)設(shè)置窗口屬性。
ViewRoot:在系統(tǒng)啟動(dòng)一個(gè)Activty組件的同時(shí)將其創(chuàng)建,類似于MVC模型中的Controller,負(fù)責(zé)管理、布局和渲染窗口UI等事務(wù)。
二、View繪制開始

系統(tǒng)啟動(dòng)一個(gè)Activity的同時(shí)創(chuàng)建一個(gè)ViewRoot實(shí)例。
Activity 在attach階段生成一個(gè)PhoneWindow對象,它包含一個(gè)DecorView對象。
在Activity執(zhí)行onCreate中的setContentView之后,將讀入的view加載進(jìn)入第一張圖的ContentViews區(qū)域。
加載完畢后觸發(fā)ViewRoot中的scheduleTraversals異步函數(shù),從而進(jìn)入ViewRoot的performTraversals函數(shù),View的繪制從這里開始。
三、performTraversals

ViewRoot中的performTraversals方法以DecorView為父容器(ViewGroup)開始自上而下的View工作流程。
View的工作流程主要是指measure、layout、draw這三大流程,即測量、布局和繪制,其中measure確定View的測量寬和高,layout確定View的最終寬/高和四個(gè)頂點(diǎn)位置,而draw則將View繪制到屏幕上。
關(guān)于measure,layout,draw的具體內(nèi)容可以看后面的文章,這里不用多在意。這個(gè)函數(shù)的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài),判斷是否重新計(jì)算視圖大小(measure)、是否重新放置視圖的位置(layout)、以及是否重繪 (draw),其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法中的哪個(gè)。
private void performTraversals() {
//1 處理mAttachInfo的初始化,并根據(jù)resize、visibility改變的情況,給相應(yīng)的變量賦值。
final View host = mView;//這里的mView即DecorView
final View.AttachInfo attachInfo = mAttachInfo;
final int viewVisibility = getHostVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
float appScale = mAttachInfo.mApplicationScale;
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
}
Rect frame = mWinFrame;
if (mFirst) {
// For the very first time, tell the view hierarchy that it
// is attached to the window. Note that at this point the surface
// object is not initialized to its backing store, but soon it
// will be (assuming the window is visible).
attachInfo.mSurface = mSurface;
attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
lp.format == PixelFormat.RGBX_8888;
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
......
}
//2 如果mLayoutRequested判斷為true,那么說明需要重新layout,不過在此之前那必須重新measure。
if (mLayoutRequested) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
if (mFirst) {
......
}
}
//3 判斷是否有子視圖的屬性發(fā)生變化,ViewRoot需要獲取這些變化。
if (attachInfo.mRecomputeGlobalAttributes) {
......
}
if (mFirst || attachInfo.mViewVisibilityChanged) {
......
}
//4 根據(jù)上面得到的變量數(shù)值,確定我們的view需要多大尺寸才能裝下。于是就得measure了,有viewgroup的weight屬性還得再做些處理
// Ask host how big it wants to be
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
mLayoutRequested = true;
}
}
//5 measure完畢,接下來可以layout了。
final boolean didLayout = mLayoutRequested;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
}
//6 如果mFirst為true,那么會(huì)進(jìn)行view獲取焦點(diǎn)的動(dòng)作。
if (mFirst) {
mRealFocusedView = mView.findFocus();
}
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
//7 終于,來到最后一步,前面的工作可以說都是鋪墊,都是為了draw而準(zhǔn)備的。
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
draw(fullRedrawNeeded)
}