UI繪制流程的起始點(diǎn) ViewRootImpl#performTraversals()方法中:
此方法里分別調(diào)用了:
///測(cè)量
performMeasure()
// 擺放布局
performLayout()
// 繪制
performDraw()
這也是我們自定義UI布局時(shí)注意的過(guò)程 : 測(cè)量(Measure)—>布局(Layout)—>繪制(Draw)
Measure測(cè)量過(guò)程:
1. 通過(guò)getRootMeasureSpec(int windowSize, int rootDimension)方法傳入父容器windowSize(具體數(shù)值) rootDimension (LayoutParams.(width|height))獲取子view寬高測(cè)量模式;
具體代碼:
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// 窗口不能調(diào)整大小。強(qiáng)制根視圖為windowSize
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// 窗口可以調(diào)整大小入偷。設(shè)置根視圖的最大大小。
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// 窗口想成為一個(gè)確切的大小廊宪。強(qiáng)制根視圖是那個(gè)大小。
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
MeasureSpec:
在Measure流程中,系統(tǒng)將View的LayoutParams根據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成對(duì)應(yīng)的MeasureSpec,在onMeasure中根據(jù)這個(gè)MeasureSpec來(lái)確定view的測(cè)量寬高
測(cè)量模式:
- EXACTLY :父容器已經(jīng)測(cè)量出所需要的精確大小阳懂,這也是childview的最終大小------match_parent,精確值
- ATMOST : child view最終的大小不能超過(guò)父容器的給的------wrap_content
- UNSPECIFIED: 不確定柜思,源碼內(nèi)部使用-------一般在ScrollView,ListView
MeasureSpec里通過(guò)和一個(gè)數(shù)值M size 模式 與或非運(yùn)算 得到一個(gè)數(shù)值(測(cè)量模式值) 而后通過(guò)M進(jìn)行一些運(yùn)算可以拿到父容器 size 模式
2. 調(diào)用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) 兩個(gè)參數(shù)是根據(jù)父容器的寬高測(cè)量模式 巷燥,在方法中 調(diào)用 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)又在這個(gè)方法里調(diào)用了onMeasure(int widthMeasureSpec, int heightMeasureSpec) 而 mView 為DecorView 而赡盘,DecorView繼承FrameLayout ,onMeasure方法在FrameLayout被重寫了缰揪,所以最終調(diào)用的是FrameLayout onMeasure方法陨享;
3. FrameLayout onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法:
容器view
1.獲取子view數(shù)并遍歷;
2.遍歷過(guò)程 獲取view child 判斷 child.getVisibility() != GONE 時(shí)調(diào)用measureChildWithMargins()方法測(cè)量子view
遍歷代碼:
for (int i = 0; i < count; i++)
final View child = getChildAt(i);//獲取子view
if (mMeasureAllChildren || child.getVisibility() != GONE) {//判斷GONE 是GONE不用測(cè)量
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);//測(cè)量child以及它的子view
final LayoutParams lp = (LayoutParams) child.getLayoutParams();//獲取view LayoutParams
//獲取所有子view中最大的寬或高
maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
2.1.measureChildWithMargins()方法中傳入了 child:子view钝腺, parentWidthMeasureSpec:父容器寬模式 parentHeightMeasureSpec:父容器高模式 等
2.2.measureChildWithMargins()具體執(zhí)行為 代碼:
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
//獲取子view MarginLayoutParams
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//根據(jù)子view寬或高對(duì)應(yīng)的 父容器模式 Padding Margin width 獲取該控件的測(cè)量模式
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//繼續(xù)測(cè)量子view 的子view 直到視圖view為止
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- 遍歷完成之后獲取了子view中最大寬 或 高 調(diào)用setMeasuredDimension()方法為該view設(shè)置寬高
setMeasuredDimension()后才可以獲得view的寬高
小結(jié):ViewGroup遍歷測(cè)量Child三方法 自定義中使用:
-
measureChildWithMargins // 有Margin測(cè)量
-
measureChild// 測(cè)量這個(gè)view 沒(méi)有Margin測(cè)量 自己遍歷
-
measureChildren//遍歷所有子view完成沒(méi)有Margin測(cè)量
視圖view
根據(jù)view設(shè)置內(nèi)容 或呈現(xiàn)內(nèi)容 來(lái)完成測(cè)量
setMeasuredDimension()調(diào)用完成測(cè)量
測(cè)量總結(jié) 自定義View抛姑,ViewGroup:
View:
- 套路:根據(jù)父容器傳來(lái)的測(cè)量模式確定view寬高 以及自己內(nèi)容 最終調(diào)用setMeasuredDimession方法來(lái)保存自己的測(cè)量寬高
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.AT_MOST:
break;
case MeasureSpec.EXACTLY:
//完成寬高測(cè)量
break;
}
setMeasuredDimension(width, height);
ViewGroup
- 1、測(cè)量子view的規(guī)格大小 measureChildWithMargins measureChild measureChildren等方法
- 2艳狐、通過(guò)子view的規(guī)格大小來(lái)確定自己的大小 setMeasuredDimession
Layout測(cè)量過(guò)程:
大概過(guò)程是Measure一樣
ViewGroup
重寫onLayout() 根據(jù)里要的樣式計(jì)算每個(gè)view left, top, right, bottom 在調(diào)用子view layout(left, top, right, bottom)方法完成布局