View繪制流程(二)
最近在學(xué)習(xí)View的繪制流程,看了幾篇不錯(cuò)的博客(ViewRootImpl的獨(dú)白晌杰,我不是一個(gè)View(布局篇)肋演、Android應(yīng)用層View繪制流程與源碼分析)自己對(duì)照源碼,梳理了一遍蜕乡。
相關(guān)類
- Activity:一個(gè)Activity是一個(gè)應(yīng)用程序組件,提供一個(gè)屏幕,用戶可以用來交互為了完成某項(xiàng)任務(wù),例如撥號(hào)层玲、拍照辛块。
- View:作為所有圖形的基類润绵。
- ViewGroup:對(duì)View繼承擴(kuò)展為視圖容器類尘盼。
- Window:它概括了Android窗口的基本屬性和基本功能。(抽象類)
- PhoneWindow:Window的子類莱衩。
- DecorView:界面的根View笨蚁,PhoneWindow的內(nèi)部類趟庄。
- ViewRootImpl:ViewRoot是GUI管理系統(tǒng)與GUI呈現(xiàn)系統(tǒng)之間的橋梁戚啥。
- WindowManangerService:簡(jiǎn)稱WMS,它的作用是管理所有應(yīng)用程序中的窗口猫十,并用于管理用戶與這些窗口發(fā)生的的各種交互拖云。
View樹的繪制流程是在ViewRootImpl類的performTraversals()方法開始的
ViewRootImpl簡(jiǎn)介
ViewRootImpl是View中的最高層級(jí)宙项,屬于所有View的根(但ViewRootImpl不是View尤筐,只是實(shí)現(xiàn)了ViewParent接口),實(shí)現(xiàn)了View和WindowManager之間的通信協(xié)議掀淘。
ViewRootImpl的初始化
WindowManager
繼承ViewManger
繁疤,從ViewManager
這個(gè)類名來看就是用來對(duì)View類進(jìn)行管理的稠腊,從ViewManager
接口中的添加架忌、更新叹放、刪除View的方法也可以看出來WindowManager
對(duì)View的管理井仰。
WindowManagerImpl
為WindowManager
的實(shí)現(xiàn)類。WindowManagerImpl
內(nèi)部方法實(shí)現(xiàn)都是由代理類WindowManagerGlobal
完成雹嗦,而WindowManagerGlobal
是一個(gè)單例了罪,也就是一個(gè)進(jìn)程中只有一個(gè)WindowManagerGlobal
對(duì)象服務(wù)于所有頁面的View泊藕。
public final class WindowManagerGlobal {
/*******部分代碼省略**********/
//所有Window對(duì)象中的View
private final ArrayList<View> mViews = new ArrayList<View>();
//所有Window對(duì)象中的View所對(duì)應(yīng)的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//所有Window對(duì)象中的View所對(duì)應(yīng)的布局參數(shù)
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
/*******部分代碼省略**********/
}
WindowManagerGlobal
在其內(nèi)部存儲(chǔ)著ViewRootImpl
和View
實(shí)例的映射關(guān)系(順序存儲(chǔ))娃圆。
在Activity的onResume之后,當(dāng)前Activity的Window對(duì)象中的View會(huì)被添加在WindowManager中臀稚。
public final class ActivityThread {
/*******部分代碼省略**********/
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
/*******部分代碼省略**********/
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
/*******部分代碼省略**********/
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
//window的類型:一個(gè)應(yīng)用窗口類型(所有的應(yīng)用窗口類型都展現(xiàn)在最頂部)吧寺。
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//將decor添加在WindowManager中
wm.addView(decor, l);
}
/*******部分代碼省略**********/
} else {
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
}
wm.addView(decor, l);
方法的具體實(shí)現(xiàn)是在WindowManager
的代理類WindowManagerGlobal
中
public final class WindowManagerGlobal {
/*******部分代碼省略**********/
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
/*******部分代碼省略**********/
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
//聲明ViwRootImpl
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
/*******部分代碼省略**********/
//創(chuàng)建ViwRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//將Window所對(duì)應(yīng)的View、ViewRootImpl赖条、LayoutParams順序添加在WindowManager中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
//把將Window所對(duì)應(yīng)的View設(shè)置給創(chuàng)建的ViewRootImpl
//通過ViewRootImpl來更新界面并完成Window的添加過程纬乍。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
/*******部分代碼省略**********/
}
}
}
創(chuàng)建ViewRootImpl
實(shí)例后仿贬,將將Window
所對(duì)應(yīng)的View
茧泪、ViewRootImpl
、LayoutParams
順序添加在WindowManager
中穴吹,然后將Window
所對(duì)應(yīng)的View
設(shè)置給創(chuàng)建的ViewRootImpl
: root.setView(view, wparams, panelParentView);
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//ViewRootImpl成員變量view進(jìn)行復(fù)制刀荒,以后操作的都是mView缠借。
mView = view;
/*******部分代碼省略**********/
//Window在添加完之前先進(jìn)行一次布局泼返,確保以后能再接受系統(tǒng)其它事件之后重新布局绅喉。
//對(duì)View完成異步刷新柴罐,執(zhí)行View的繪制方法革屠。
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//將該Window添加到屏幕似芝。
//mWindowSession實(shí)現(xiàn)了IWindowSession接口党瓮,它是Session的客戶端Binder對(duì)象.
//addToDisplay是一次AIDL的跨進(jìn)程通信寞奸,通知WindowManagerService添加IWindow
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
} catch (RemoteException e) {
/*******部分代碼省略**********/
} finally {
if (restore) {
attrs.restore();
}
}
/*******部分代碼省略**********/
//設(shè)置當(dāng)前View的mParent
view.assignParent(this);
/*******部分代碼省略**********/
}
}
}
}
requestLayout();
方法請(qǐng)求view繪制枪萄,其過程主要是在ViewRootImpl
的performTraversals
方法中呻引。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
//請(qǐng)求對(duì)界面進(jìn)行布局
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
/*******部分代碼省略**********/
//安排任務(wù)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
//做任務(wù)
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
try {
//執(zhí)行任務(wù)
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
}
整個(gè)View樹的繪圖流程是在ViewRootImpl
類的performTraversals()
方法(這個(gè)方法巨長(zhǎng))開始的元践,該方法做的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài)单旁,判斷是否重新計(jì)算視圖大小(measure)
象浑、是否重新放置視圖的位置(layout)
愉豺、以及是否重繪 (draw)
蚪拦,其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法驰贷。
private void performTraversals() {
......
//最外層的根視圖的widthMeasureSpec和heightMeasureSpec由來
//lp.width和lp.height在創(chuàng)建ViewGroup實(shí)例時(shí)等于MATCH_PARENT
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
......
}
這里的最外層根視圖是
DecorView
括袒,也就是mView
锹锰,在WindowManagerGlobal
中的addview
中傳遞過來的城须。
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY);
break;
......
}
return measureSpec;
}
該方法的是用來測(cè)Root View的米苹。上面?zhèn)魅雲(yún)?shù)后這個(gè)函數(shù)走的是
MATCH_PARENT
,使用MeasureSpec.makeMeasureSpec
方法組裝一個(gè)MeasureSpec
砰琢,MeasureSpec
的specMode
等于EXACTLY蘸嘶,specSize
等于windowSize,也就是為何根視圖總是全屏的原因陪汽。
View的測(cè)量
ViewRootImpl
調(diào)用performMeasure
執(zhí)行Window對(duì)應(yīng)的View的測(cè)量训唱。
- ViewRootImpl的
performMeasure
;- DecorView(FrameLayout)的
measure
;- DecorView(FrameLayout)的
onMeasure
;- DecorView(FrameLayout)所有子View的
measure
;
private fun performMeasure(childWidthMeasureSpec: Int, childHeightMeasureSpec: Int) {
if (mView == null) {
return
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure")
try {
//mView在Activity中為DecorView(FrameLayout)
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW)
}
}
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
......
//final方法挚冤,子類不可重寫
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
//回調(diào)onMeasure()方法
onMeasure(widthMeasureSpec, heightMeasureSpec);
......
}
}
為整個(gè)View樹計(jì)算實(shí)際的大小训挡,然后設(shè)置實(shí)際的高和寬为肮,每個(gè)View控件的實(shí)際寬高都是由父視圖和自身決定的茅特。實(shí)際的測(cè)量是在
onMeasure
方法進(jìn)行,所以在View的子類需要重寫onMeasure
方法兵睛,這是因?yàn)?code>measure方法是final的,不允許重載突琳,所以View子類只能通過重載onMeasure
來實(shí)現(xiàn)自己的測(cè)量邏輯。
int widthMeasureSpec
:他由兩部分組成镜豹,高2位表示MODE例衍,定義在MeasureSpec類(View的內(nèi)部類)中硼一,有三種類型,MeasureSpec.EXACTLY表示確定大小哼蛆, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不確定橄抹。低30位表示size,也就是父View的大小疟羹。對(duì)于系統(tǒng)Window類的DecorVIew對(duì)象Mode一般都為MeasureSpec.EXACTLY ,而size分別對(duì)應(yīng)屏幕寬高愧杯。對(duì)于子View來說大小是由父View和子View共同決定的。
//View的onMeasure默認(rèn)實(shí)現(xiàn)方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
onMeasure
默認(rèn)的實(shí)現(xiàn)僅僅調(diào)用了setMeasuredDimension
,它對(duì)View的成員變量mMeasuredWidth
和mMeasuredHeight
變量賦值抵乓,measure
的主要目的就是對(duì)View樹中的每個(gè)View的mMeasuredWidth
和mMeasuredHeight
進(jìn)行賦值,所以一旦這兩個(gè)變量被賦值意味著該View的測(cè)量工作結(jié)束败徊。
默認(rèn)的尺寸大小即傳入的參數(shù)都是通過getDefaultSize
返回的眷蜈,我們就看一下該方法的實(shí)現(xiàn)。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
//通過MeasureSpec解析獲取mode與size
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
specMode
等于AT_MOST或EXACTLY就返回specSize
。
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
建議的最小寬度和高度都是由View的Background尺寸與通過設(shè)置View的
miniXXX
屬性共同決定的榴啸。也只有當(dāng)Mode為MeasureSpec.UNSPECIFIED時(shí)才會(huì)使用該尺寸坦报。
到此一次最基礎(chǔ)的元素View的measure
過程就完成了潜的。
View實(shí)際是嵌套的,而且measure是遞歸傳遞的,所以每個(gè)View都需要measure
政己,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了measureChildren
, measureChild
, measureChildWithMargins
方法來對(duì)子視圖進(jìn)行測(cè)量,measureChildren
內(nèi)部實(shí)質(zhì)只是循環(huán)調(diào)用measureChild
释牺,measureChild
和measureChildWithMargins
的區(qū)別就是是否把margin和padding也作為子視圖的大小。ViewGroup本身不調(diào)用measureChildWithMargins
和measureChildren
方法祭刚,由繼承類通過for循環(huán)調(diào)用此方法進(jìn)行子View的測(cè)量憔古。下面看一下ViewGroup中稍微復(fù)雜的measureChildWithMargins
方法锯梁。
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
//獲取子視圖的LayoutParams
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//調(diào)整MeasureSpec
//通過這兩個(gè)參數(shù)以及本身的LayoutParams來共同決定子視圖的測(cè)量規(guī)則
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);
//調(diào)運(yùn)子View的measure方法,子View的measure中會(huì)回調(diào)子View的onMeasure方法
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
該方法就是對(duì)父視圖提供的
measureSpec
參數(shù)結(jié)合子視圖的LayoutParams參數(shù)進(jìn)行了調(diào)整,然后再來調(diào)用child.measure()
方法充岛,具體通過方法getChildMeasureSpec
來進(jìn)行參數(shù)調(diào)整垒在。
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//獲取當(dāng)前Parent View的Mode和Size
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
//獲取Parent size與padding差值(也就是Parent剩余大小)伞鲫,若差值小于0直接返回0
int size = Math.max(0, specSize - padding);
//定義返回值存儲(chǔ)變量
int resultSize = 0;
int resultMode = 0;
//依據(jù)當(dāng)前Parent的Mode進(jìn)行switch分支邏輯
switch (specMode) {
// Parent has imposed an exact size on us
//默認(rèn)Root View的Mode就是EXACTLY
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
//如果child的layout_wOrh屬性在xml或者java中給予具體大于等于0的數(shù)值
//設(shè)置child的size為真實(shí)layout_wOrh屬性值,mode為EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//如果child的layout_wOrh屬性在xml或者java中給予MATCH_PARENT
// Child wants to be our size. So be it.
//設(shè)置child的size為size诵肛,mode為EXACTLY
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//如果child的layout_wOrh屬性在xml或者java中給予WRAP_CONTENT
//設(shè)置child的size為size,mode為AT_MOST
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
......
//其他Mode分支類似
}
//將mode與size通過MeasureSpec方法整合為32位整數(shù)返回
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
getChildMeasureSpec
的邏輯是通過其父View提供的MeasureSpec
參數(shù)得到specMode
和specSize
乙埃,然后根據(jù)計(jì)算出來的specMode
以及子View的childDimension
(layout_width或layout_height)來計(jì)算自身的measureSpec
出吹,如果其本身包含子視圖捶牢,則計(jì)算出來的measureSpec
將作為調(diào)用其子視圖measure
函數(shù)的參數(shù)渐排,同時(shí)也作為自身調(diào)用setMeasuredDimension
的參數(shù)吓歇,如果其不包含子視圖則默認(rèn)情況下最終會(huì)調(diào)用onMeasure
的默認(rèn)實(shí)現(xiàn),并最終調(diào)用到setMeasuredDimension
炼鞠。
最終決定View的
measure
大小是View的setMeasuredDimension
方法,所以我們可以通過setMeasuredDimension
設(shè)定死值來設(shè)置View的mMeasuredWidth和mMeasuredHeight的大小,但是一個(gè)好的自定義View應(yīng)該會(huì)根據(jù)子視圖的measureSpec
來設(shè)置mMeasuredWidth和mMeasuredHeight的大小观游,這樣的靈活性更大
View測(cè)量總結(jié)
在Activity的onResume
之后,當(dāng)前Activity的Window對(duì)象中的View(DecorView)會(huì)被添加在WindowManager中。也就是在ActivityThread的handleResumeActivity
方法中調(diào)用wm.addView(decor, l);
將DecorView添加到WindowManager中;
WindowManager繼承ViewManager,它的實(shí)現(xiàn)類為WindowManagerImpl端圈,該類中的方法的具體實(shí)現(xiàn)是由其代理類WindowManagerGlobal實(shí)現(xiàn)的宴倍;
在它的addView
方法中會(huì)創(chuàng)建ViewRootImpl的實(shí)例脖捻,然后將Window對(duì)應(yīng)的View(DecorView)羡亩,ViewRootImpl专挪,LayoutParams順序添加在WindowManager中率寡,最后將Window所對(duì)應(yīng)的View設(shè)置給創(chuàng)建的ViewRootImpl捅僵,通過ViewRootImpl來更新界面并完成Window的添加過程庙楚;
設(shè)置view調(diào)用的是ViewRootImpl的setView
方法逛薇,在該方法中調(diào)用requestLayout();
方法來異步執(zhí)行view的繪制方法;之后將Window添加到屏幕,通過WMS(跨進(jìn)程通信)
在requestLayout
方法中最終會(huì)調(diào)用ViewRootImpl的performTraversals();
方法坯临,該方法做的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài),判斷是否重新計(jì)算視圖大小(measure)
婿滓、是否重新放置視圖的位置(layout)
卿吐、以及是否重繪 (draw)
芥丧,其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法:performMeasure
乖仇、performLayout
、performDraw
;
在performMeasure
方法中調(diào)用的是View的measure
方法记劝,該方法是final修飾砍鸠,不能被子類重寫,在該方法中實(shí)際調(diào)用的是View的onMeasure
方法,子類可以重寫onMeasure
方法來實(shí)現(xiàn)自己的測(cè)量規(guī)則。
View默認(rèn)的onMeasure
方法很簡(jiǎn)單只是調(diào)用了setMeasuredDimension
方法后裸,該方法的作用是給View的成員變量mMeasuredWidth和mMeasuredHeight賦值,View的測(cè)量主要就是給這兩個(gè)變量賦值蘸秘,這兩個(gè)變量一旦賦值毛秘,也就意味著測(cè)量過程的結(jié)束。
setMeasuredDimension
方法傳入的尺寸是通過getDefaultSize(int size, int measureSpec);
方法返回的,在
getDefaultSize
方法中解析measureSpec的Mode和Size,如果Mode為MeasureSpec.AT_MOST或者MeasureSpec.EXACTLY,最終的size的值為解析后的size钦听;如果Mode為MeasureSpec.UNSPECIFIED衣撬,最終的size為建議的最小值=getSuggestedMinimumWidth
,該方法的具體實(shí)現(xiàn)為return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
松蒜,建議的最小寬度和高度都是由View的Background尺寸與通過設(shè)置View的miniXXX
屬性共同決定的
measureSpec是由getRootMeasureSpec
方法決定的:measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY);
根布局的大小是Window的大小扔茅,Window大小是不能改變的,總是全屏的秸苗。
View實(shí)際是嵌套的咖摹,而且measure是遞歸傳遞的,所以每個(gè)View都需要measure难述,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了measureChildren
, measureChild
, measureChildWithMargins
方法來對(duì)子視圖進(jìn)行測(cè)量吐句,measureChildren
內(nèi)部實(shí)質(zhì)只是循環(huán)調(diào)用measureChild
胁后,measureChild
和measureChildWithMargins
的區(qū)別就是是否把margin和padding也作為子視圖的大小。
measureChildWithMargins
方法的作用就是對(duì)父View提供的measureSpec參數(shù)結(jié)合子View的LayoutParams參數(shù)進(jìn)行了調(diào)整嗦枢,然后再來調(diào)用child.measure()
方法攀芯,具體通過方法getChildMeasureSpec
方法來進(jìn)行參數(shù)調(diào)整。計(jì)算出來自身的measureSpec作為調(diào)用其子視圖measure
方法的參數(shù)文虏,同時(shí)也作為自身調(diào)用setMeasuredDimension
的參數(shù)侣诺,如果其不包含子視圖則默認(rèn)情況下最終會(huì)調(diào)用onMeasure
的默認(rèn)實(shí)現(xiàn),并最終調(diào)用到setMeasuredDimension
氧秘。
最終決定View的measure大小是View的setMeasuredDimension
方法年鸳,該方法就是設(shè)置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在onMeasure
方法調(diào)用setMeasuredDimension
之前調(diào)整了measureSpec丸相。