Activity的布局文件是如何被加載的呢?
1)setContentView(R.layout.main) 通過這個(gè)方法開始了我們的布局渲染。在這個(gè)方法內(nèi)部,又一個(gè)getWindow()方法,這個(gè)window是一個(gè)抽象類,它僅僅有一個(gè)實(shí)現(xiàn)類就是phoneWindow,它是系統(tǒng)中窗口的頂級(jí)類。
2)getWindow().setContentView(int layoutId) 如果原有的加載容器不為null,說明原來頁(yè)面上有內(nèi)容,mContentParent.removeAllViews();,mLayoutInflater.inflate(layoutResID,mContentParent); mContentParent==null時(shí),加載installDecor()來初始化mContentParent,查看installDecor的源碼可以發(fā)現(xiàn),generateDecor()初始化了DecorView,DecorView是一個(gè)PhoneWindow的內(nèi)部類繼承自Framelayout。mContentParent則是由generateLayout()生成的,內(nèi)部源碼含有WindowStyle,title、ActionBar設(shè)置,開始初始化mContentParent。View in= mLayoutInflater.inflate(layoutResource,null);實(shí)例化contentParent。這就是PhoneWindow,DecorView下的一個(gè)View,包含兩個(gè)子view,一個(gè)是裝在狀態(tài)欄的,一個(gè)是布局文件。
3)到目前位置,通過setContentView實(shí)例化了DecorView并且加載了設(shè)置進(jìn)來的布局文件。而并沒有任何測(cè)量、布局、繪制的工作,接下來通ActivityThread源碼來探索進(jìn)一步如何到達(dá)的三步曲。ActivityThread.handleLaunchActivity()啟動(dòng)我們的activity,performlaunchActivity之后,activity的oncreate被調(diào)用,我們的資源文件不加載,但是此時(shí)還是不可見的,也就還沒有測(cè)量等的工作。handlerResumeActivity之后,我們的頂層視圖DecorView通過windowmanager加載到了window中。
Window 是一個(gè)抽象概念,一個(gè)window對(duì)應(yīng)一個(gè)view和ViewRootImpl;
Window和View是通過ViewRootImpl聯(lián)系起來的;
ViewRootImpl才是一個(gè)View實(shí)現(xiàn)的動(dòng)作;
WindowManager中也有一個(gè)WindowManagerImpl作為實(shí)現(xiàn)的類,負(fù)責(zé)具體的操作。
WindowMangerImpl.addView方法 內(nèi)部WindowMangerGlobal的一個(gè)內(nèi)部實(shí)例。addView()源碼,通過DecorView獲取上下文以及傳入display實(shí)例化一個(gè)ViewRootImpl對(duì)象,也就是說ViewRootImpl與DecorView關(guān)聯(lián)起來了。調(diào)用了ViewRootImpl的setView方法,聲明輸入事件的管道,DisplayManager的注冊(cè),view的繪畫,window的添加等。
作為繪制view的入口,requestLayout()方法內(nèi)部有個(gè)scheduleTraversals(),系統(tǒng)會(huì)發(fā)起一個(gè)異步消息,然后在異步消息執(zhí)行過程中調(diào)用performTraversals()完成具體view樹遍歷。performMeasure、performLayout、performDraw方法進(jìn)入執(zhí)行。
布局的測(cè)量過程
測(cè)量布局繪制
MeasureSpec是View內(nèi)部的一個(gè)靜態(tài)類,EXACTLY(match_parent) 這種狀態(tài)下的控件的大小是明確的。AT_MOST(wrap_content) 父控件對(duì)子控件說,我還不知道你的大小,我給你自由,我的地方是這么大,你按你的意愿來,但最大也只能跟我一樣大了。
viewGroup測(cè)量下面的childView的方法,measureChildWithMargins(View child,int parentWidthMeasureSpec,int widthUsed,int parentHeightMeasureSpec,int heightUsed)
MarginLayoutParams 獲取子view的Layout參數(shù),因?yàn)樽觱iew的大小也跟布局參數(shù)相關(guān)。測(cè)量childView的寬的measureSpec,第一個(gè)參數(shù)會(huì)傳入parent的measureSpec,第二個(gè)參數(shù)經(jīng)過計(jì)算后實(shí)際得到的是parent已被使用的寬度和child的padding和margin消耗的寬度,第三個(gè)參數(shù)為child的大小,這個(gè)大小并不一定是child最后的大小,只能說是我們希望創(chuàng)建的大小。
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin+widthUsed,lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin+heighUsed,lp.height);
child.measure(childWidthMeasureSpec,childHeightMeasuerSpec);
1)getChildMeasureSpec()源碼分析
int specMode = MeasureSpec.getMode();
int specSize = MeasureSpec.getSize();
int size = Math.max(0,specSize - padding); // 剩余的可用大小
int resultSize; int resultMode;
case specMode == MeasureSpec.EXACTLY
if(childDimension>=0){
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
}
else if(childDimension == LayoutParams.MATCH_PARENT){
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
}
else if(childDimension == LayoutParams.WRAP_CONTENT){
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
case specMode == MeasureSpec.AT_MOST
if(childDimension >=0){
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
}else if(childDimension == LayoutParams.MATCH_PARENT){
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}else if(childDimension == LayoutParams.WRAP_CONTENT){
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}

MeasureSpec的mode和size是根據(jù)parent和child相互決定的。
調(diào)用setMeasuredDimension設(shè)置view的大小
調(diào)用getDefaultSize獲取View的大小,
getSuggestedMinimumWidth獲取一個(gè)建議最小值
onMeasure-> setMeasuredDimension-> getDefaultSize->getSuggestedMinimumWidth
Layout過程
1)performTraversals內(nèi)部調(diào)用performLayout開始執(zhí)行布局工作
2)performLayout內(nèi)部會(huì)調(diào)用layout開始進(jìn)行布局
3)layout中會(huì)調(diào)用setFrame確定mTop,mLeft,mRight,mBottom的值以及判斷是個(gè)點(diǎn)的值是否發(fā)生了變化
4)最后調(diào)用onLayout方法通知下面的childView進(jìn)行布局操作
布局流程主要的操作就是確定View的四個(gè)點(diǎn)的數(shù)值
Draw過程
較為簡(jiǎn)單,主要流程見下圖。
draw.png