Android進(jìn)階之深入理解View的測(cè)量(Measure)流程機(jī)制
前言
View 的工作原理中最重要的就是測(cè)量、布局、繪制三大過程,而其中測(cè)量是最復(fù)雜的;
那么我們就來介紹下View 的測(cè)量過程;
一、MeasureSpec
測(cè)量自身的大小的時(shí)候,會(huì)執(zhí)行measure(int widthMeasureSpec, int heightMeasureSpec)方法。注意方法中兩個(gè)參數(shù),它們其實(shí)是一個(gè)int 類型的MeasureSpec;
1、specMode
測(cè)量模式分為三種:
- UNSPECIFIED模式:本質(zhì)就是不限制模式,父視圖不對(duì)子View進(jìn)行任何約束,View想要多大要多大,想要多長(zhǎng)要多長(zhǎng);
- EXACTLY模式:該模式其實(shí)對(duì)應(yīng)的場(chǎng)景就是match_parent或者是一個(gè)具體的數(shù)據(jù)(50dp或80px),父視圖為子View指定一個(gè)確切的大小,無論子View的值設(shè)置多大,都不能超出父視圖的范圍;
- AT_MOST模式:這個(gè)模式對(duì)應(yīng)的場(chǎng)景就是wrap_content,其內(nèi)容就是父視圖給子View設(shè)置一個(gè)最大尺寸,子View只要不超過這個(gè)尺寸即可;
2、MeasureSpec
View的MeasureSpec值是根據(jù)子View的布局參數(shù)(LayoutParams)和父容器的MeasureSpec至計(jì)算而來的,其具體邏輯封裝在了getChildMeasureSpec()方法中
- public static int getChildMeasureSpec(
- int spec, int padding, int childDimension) {
- //1、獲取parent的specMode
- int specMode = MeasureSpec.getMode(spec);
- //2、獲取parent的specSize
- int specSize = MeasureSpec.getSize(spec);
- //3、size=剩余的可用大小
- int size = Math.max(0, specSize - padding);
- int resultSize = 0;
- int resultMode = 0;
- //4、通過switch語句判斷parent的集中mode,分別處理
- switch (specMode) {
- // 5、parent為MeasureSpec.EXACTLY時(shí)
- case MeasureSpec.EXACTLY:
- if (childDimension >= 0) {
- //5.1、當(dāng)childDimension大于0時(shí),表示child的大小是
- //明確指出的,如layout_width= "100dp";
- // 此時(shí)child的大小= childDimension,
- resultSize = childDimension;
- //child的測(cè)量模式= MeasureSpec.EXACTLY
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- //5.2、此時(shí)為L(zhǎng)ayoutParams.MATCH_PARENT
- //也就是 android:layout_width="match_parent"
- //因?yàn)閜arent的大小是明確的,child要匹配parent的大小
- //那么我們就直接讓child=parent的大小就好
- resultSize = size;
- //同樣,child的測(cè)量模式= MeasureSpec.EXACTLY
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- //5.3、此時(shí)為L(zhǎng)ayoutParams.WRAP_CONTENT
- //也就是 android:layout_width="wrap_content"
- // 這個(gè)模式需要特別對(duì)待,child說我要的大小剛好夠放
- //需要展示的內(nèi)容就好,而此時(shí)我們并不知道child的內(nèi)容
- //需要多大的地方,暫時(shí)先把parent的size給他
- resultSize = size;
- //自然,child的mode就是MeasureSpec.AT_MOST的了
- resultMode = MeasureSpec.AT_MOST;
- }
- break;
- // 5、parent為AT_MOST,此時(shí)child最大不能超過parent
- case MeasureSpec.AT_MOST:
- if (childDimension >= 0) {
- //同樣child大小明確時(shí),
- //大小直接時(shí)指定的childDimension
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- // child要跟parent一樣大,resultSize=可用大小
- resultSize = size;
- //因?yàn)閜arent是AT_MOST,child的大小也還是未定的,
- //所以也是MeasureSpec.AT_MOST
- resultMode = MeasureSpec.AT_MOST;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- //又是特殊情況,先給child可用的大小
- resultSize = size;
- resultMode = MeasureSpec.AT_MOST;
- }
- break;
- // 這種模式是很少用的,我們也看下吧
- case MeasureSpec.UNSPECIFIED:
- if (childDimension >= 0) {
- // 與前面同樣的處理
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- // Child wants to be our size... find out how big it should
- // be
- resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
- resultMode = MeasureSpec.UNSPECIFIED;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- // Child wants to determine its own size.... find out how
- // big it should be
- resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
- resultMode = MeasureSpec.UNSPECIFIED;
- }
- break;
- }
- //通過傳入resultSize和resultMode生成一個(gè)MeasureSpec.返回
- return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
- }
- ①當(dāng)子View采用具體數(shù)值(dp / px)時(shí):無論父容器的測(cè)量模式是什么,子View的測(cè)量模式都是EXACTLY且大小等于設(shè)置的具體數(shù)值;
- ②當(dāng)子View采用match_parent時(shí):子View的測(cè)量模式與父容器的測(cè)量模式一致;若測(cè)量模式為EXACTLY,則子View的大小為父容器的剩余空間;若測(cè)量模式為AT_MOST,則子View的大小不超過父容器的剩余空間;
- ③當(dāng)子View采用wrap_parent時(shí):如果父容器測(cè)量模式為UNSPECIFIED,子View也為UNSPECIFIED,否則子View為AT_MOST且大小不超過父容器的剩余空間;
二、view測(cè)量過程
1、performMeasure()
加載好系統(tǒng)布局資源后,會(huì)觸發(fā)ViewRootImpl的performTraversals()方法,該方法內(nèi)容會(huì)開始執(zhí)行測(cè)量、布局和繪制的工作,我們來看這個(gè)方法的源碼關(guān)鍵部分:
- private void performTraversals() {
- ...
- if (!mStopped) {
- //獲取頂層布局的childWidthMeasureSpec
- int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
- //獲取頂層布局的childHeightMeasureSpec
- int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
- //測(cè)量開始測(cè)量
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
- }
- if (didLayout) {
- //執(zhí)行布局方法
- performLayout(lp, desiredWindowWidth, desiredWindowHeight);
- ...
- }
- if (!cancelDraw && !newSurface) {
- ...
- //開始繪制了哦
- performDraw();
- }
- }
- }
整個(gè)方法內(nèi)部其實(shí)就是做了一些基礎(chǔ)的判斷后,再順序的調(diào)用測(cè)量、布局和繪制的相關(guān)方法,從而完成自定義View的整個(gè)工作流程;
performTraversals方法,使用的是getRootMeasureSpec方法來獲取子View的MeasureSpec;
整個(gè)Activity的頂層View其實(shí)就是一個(gè)DecorView,所以這里獲取的其實(shí)是DeorView的MeasureSpec,然后將其傳入performMeasure方法中去開始測(cè)量,現(xiàn)在看看PerformMeasure方法:
- private void performMeasure(int childWidthMeasureSpec,
- int childHeightMeasureSpec) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
- try {
- //mView其實(shí)就是我們的頂層DecorView,從DecorView開始測(cè)量
- mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- }
2、measure()
源碼中找到了measure方法
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- boolean optical = isLayoutModeOptical(this);
- if (optical != isLayoutModeOptical(mParent)) {
- Insets insets = getOpticalInsets();
- int oWidth = insets.left + insets.right;
- int oHeight = insets.top + insets.bottom;
- widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
- heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
- }
- // Suppress sign extension for the low bytes
- long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
- if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
- final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
- // Optimize layout by avoiding an extra EXACTLY pass when the view is
- // already measured as the correct size. In API 23 and below, this
- // extra pass is required to make LinearLayout re-distribute weight.
- final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
- || heightMeasureSpec != mOldHeightMeasureSpec;
- final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
- && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
- final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
- && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
- final boolean needsLayout = specChanged
- && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
- if (forceLayout || needsLayout) {
- // first clears the measured dimension flag
- mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
- resolveRtlPropertiesIfNeeded();
- int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
- if (cacheIndex < 0 || sIgnoreMeasureCache) {
- // measure ourselves, this should set the measured dimension flag back
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- } else {
- long value = mMeasureCache.valueAt(cacheIndex);
- // Casting a long to int drops the high 32 bits, no mask needed
- setMeasuredDimensionRaw((int) (value >> 32), (int) value);
- mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- }
- // flag not set, setMeasuredDimension() was not invoked, we raise
- // an exception to warn the developer
- if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
- throw new IllegalStateException("View with id " + getId() + ": "
- + getClass().getName() + "#onMeasure() did not set the"
- + " measured dimension by calling"
- + " setMeasuredDimension()");
- }
- mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
- }
- mOldWidthMeasureSpec = widthMeasureSpec;
- mOldHeightMeasureSpec = heightMeasureSpec;
- mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
- (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
- }
View的測(cè)量是View.onMeasure而ViewGroup的測(cè)量則是XXLayout.onMeasure,這兩種onMeasure方法的實(shí)現(xiàn)是不同的
3、View.onMeasure()
獲取一個(gè)建議最小值;
調(diào)用getDefaultSize方法定義對(duì)View尺寸的測(cè)量邏輯;
調(diào)用setMeasureDimension()儲(chǔ)存測(cè)量后的View寬/高;
- protected void onMeasure(int widthMeasureSpec,
- int heightMeasureSpec) {
- setMeasuredDimension(
- getDefaultSize(getSuggestedMinimumWidth(),
- widthMeasureSpec),
- getDefaultSize(getSuggestedMinimumHeight(),
- heightMeasureSpec));
- }
- getSuggestedMinimumWidth()
- protected int getSuggestedMinimumWidth() {
- return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
- }
- 前View是否有背景?沒有就返回android:minWidth設(shè)置的值:有就返回android:minWidth和mBackground.getMinimumWidth()中較大的那個(gè)值;
- public int getMinimumWidth() {
- final int intrinsicWidth = getIntrinsicWidth();
- //返回背景圖Drawable的原始寬度
- return intrinsicWidth > 0 ? intrinsicWidth :0 ;
- }
getIntrinsicWidth()獲取的是背景圖的原始寬度,背景圖是BitmapDrawable則有原始寬度,在沒有原始寬度的情況下則返回0;
4、getDefaultSize()
- public static int getDefaultSize(int size, int measureSpec) {
- int result = size;
- //1、獲得MeasureSpec的mode
- int specMode = MeasureSpec.getMode(measureSpec);
- //2、獲得MeasureSpec的specSize
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (specMode) {
- case MeasureSpec.UNSPECIFIED:
- //這個(gè)我們先不看他
- result = size;
- break;
- case MeasureSpec.AT_MOST:
- case MeasureSpec.EXACTLY:
- //3、可以看到,最終返回的size就是我們MeasureSpec中測(cè)量得到的size
- result = specSize;
- break;
- }
- return result;
- }
測(cè)量模式是AT_MOST還是EXACTLY,最終返回的Size是一樣的;
5、setMeasureDimension
該方法是儲(chǔ)存測(cè)量后的View的寬和高的,在自定義View的時(shí)候,我們自己重寫的onMeasure方法最后一定要調(diào)用這個(gè)方法,否則會(huì)報(bào)錯(cuò)
- protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
- //1、判斷是否使用視覺邊界布局
- boolean optical = isLayoutModeOptical(this);
- //2、判斷view和parentView使用的視覺邊界布局是否一致
- if (optical != isLayoutModeOptical(mParent)) {
- //不一致時(shí)要做一些邊界的處理
- Insets insets = getOpticalInsets();
- int opticalWidth = insets.left + insets.right;
- int opticalHeight = insets.top + insets.bottom;
- measuredWidth += optical ? opticalWidth : -opticalWidth;
- measuredHeight += optical ? opticalHeight : -opticalHeight;
- }
- //3、重點(diǎn)來了,經(jīng)過過濾之后調(diào)用了setMeasuredDimensionRaw方法,看來應(yīng)該是這個(gè)方法設(shè)置我們的view的大小
- setMeasuredDimensionRaw(measuredWidth, measuredHeight);
- }
- setMeasureDimensionRaw方法:
- private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
- //最終將測(cè)量好的大小存儲(chǔ)到mMeasuredWidth和mMeasuredHeight上,所以在測(cè)量之后
- //我們可以通過調(diào)用getMeasuredWidth獲得測(cè)量的寬、getMeasuredHeight獲得高
- mMeasuredWidth = measuredWidth;
- mMeasuredHeight = measuredHeight;
- mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
- }
以上就是View的測(cè)量過程,其順序?yàn)椋?/p>
performTraversals->performMeasure->measure->onMeasure-> setMeasuredDimension-> setMeasuredDimensionRaw,由setMeasuredDimensionRaw最終保存測(cè)量的數(shù)據(jù)
三、ViewGroup的測(cè)量過程詳解
1、measureChildren()
作用就是遍歷子View并調(diào)用measureChild()進(jìn)行下一步測(cè)量
- protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
- //參數(shù)說明:父視圖的測(cè)量規(guī)格(MeasureSpec)
- final int size = mChildrenCount;
- final View[] children = mChildren;
- //遍歷所有的子view
- for (int i = 0; i < size; ++i) {
- final View child = children[i];
- //如果View的狀態(tài)不是GONE就調(diào)用measureChild()去進(jìn)行下一步的測(cè)量
- if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
- measureChild(child, widthMeasureSpec, heightMeasureSpec);
- }
- }
- }
2、measureChild()
作用就是計(jì)算單個(gè)子View的MeasureSpec,調(diào)用子View的measure進(jìn)行每個(gè)子View最后的寬、高的測(cè)量
- protected void measureChild(View child, int parentWidthMeasureSpec,
- int parentHeightMeasureSpec) {
- // 獲取子視圖的布局參數(shù)
- final LayoutParams lp = child.getLayoutParams();
- // 調(diào)用getChildMeasureSpec(),根據(jù)父視圖的MeasureSpec & 布局參數(shù)LayoutParams,計(jì)算單個(gè)子View的MeasureSpec
- // getChildMeasureSpec()請(qǐng)回看上面的解析
- // 獲取 ChildView 的 widthMeasureSpec
- final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
- mPaddingLeft + mPaddingRight, lp.width);
- // 獲取 ChildView 的 heightMeasureSpec
- final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
- mPaddingTop + mPaddingBottom, lp.height);
- // 將計(jì)算好的子View的MeasureSpec值傳入measure(),進(jìn)行最后的測(cè)量
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
3、measure()
和View的measure一致
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- ...
- int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
- mMeasureCache.indexOfKey(key);
- if (cacheIndex < 0 || sIgnoreMeasureCache) {
- // 調(diào)用onMeasure()計(jì)算視圖大小
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- } else {
- ...
- }
4、XXXLayout.onMeasure()
- ViewGroup的onMeasure和View的onMeasure是不同的,究其原因其實(shí)是因?yàn)閂iewGroup是一個(gè)抽象類,所以即便它繼承了View也不用必須實(shí)現(xiàn)View中的onMeasure方法,而它的子類不具備通用的布局特性,這導(dǎo)致他們的子View的測(cè)量方法各不相同,因此,ViewGroup無法對(duì)onMeasure()做統(tǒng)一的實(shí)現(xiàn)
- FrameLayout為例,看看它的onMeasure是如何實(shí)現(xiàn)的:
- //這里的widthMeasureSpec、heightMeasureSpec
- //其實(shí)就是我們frameLayout可用的widthMeasureSpec 、
- //heightMeasureSpec
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- //1、獲得frameLayout下childView的個(gè)數(shù)
- int count = getChildCount();
- //2、看這里的代碼我們可以根據(jù)前面的Measure圖來進(jìn)行分析,因?yàn)橹灰猵arent
- //不是EXACTLY模式,以frameLayout為例,假設(shè)frameLayout本身還不是EXACTL模式,
- // 那么表示他的大小此時(shí)還是不確定的,從表得知,此時(shí)frameLayout的大小是根據(jù)
- //childView的最大值來設(shè)置的,這樣就很好理解了,也就是childView測(cè)量好后還要再
- //測(cè)量一次,因?yàn)榇藭r(shí)frameLayout的值已經(jīng)可以算出來了,對(duì)于child為MATCH_PARENT
- //的,child的大小也就確定了,理解了這里,后面的代碼就很 容易看懂了
- final boolean measureMatchParentChildren =
- MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
- MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
- //3、清理存儲(chǔ)模式為MATCH_PARENT的child的隊(duì)列
- mMatchParentChildren.clear();
- //4、下面三個(gè)值最終會(huì)用來設(shè)置frameLayout的大小
- int maxHeight = 0;
- int maxWidth = 0;
- int childState = 0;
- //5、開始便利frameLayout下的所有child
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- //6、小發(fā)現(xiàn)哦,只要mMeasureAllChildren是true,就算child是GONE也會(huì)被測(cè)量哦,
- if (mMeasureAllChildren || child.getVisibility() != GONE) {
- //7、開始測(cè)量childView
- measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
- //8、下面代碼是獲取child中的width 和height的最大值,后面用來重新設(shè)置frameLayout,有需要的話
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- 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());
- //9、如果frameLayout不是EXACTLY,
- if (measureMatchParentChildren) {
- if (lp.width == LayoutParams.MATCH_PARENT ||
- lp.height == LayoutParams.MATCH_PARENT) {
- //10、存儲(chǔ)LayoutParams.MATCH_PARENT的child,因?yàn)楝F(xiàn)在還不知道frameLayout大小,
- //也就無法設(shè)置child的大小,后面需重新測(cè)量
- mMatchParentChildren.add(child);
- }
- }
- }
- }
- ....
- //11、這里開始設(shè)置frameLayout的大小
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- resolveSizeAndState(maxHeight, heightMeasureSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
- //12、frameLayout大小確認(rèn)了,我們就需要對(duì)寬或高為L(zhǎng)ayoutParams.MATCH_PARENTchild重新測(cè)量,設(shè)置大小
- count = mMatchParentChildren.size();
- if (count > 1) {
- for (int i = 0; i < count; i++) {
- final View child = mMatchParentChildren.get(i);
- final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
- final int childWidthMeasureSpec;
- if (lp.width == LayoutParams.MATCH_PARENT) {
- final int width = Math.max(0, getMeasuredWidth()
- - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- - lp.leftMargin - lp.rightMargin);
- //13、注意這里,為child是EXACTLY類型的childWidthMeasureSpec,
- //也就是大小已經(jīng)測(cè)量出來了不需要再測(cè)量了
- //通過MeasureSpec.makeMeasureSpec生成相應(yīng)的MeasureSpec
- childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- width, MeasureSpec.EXACTLY);
- } else {
- //14、如果不是,說明此時(shí)的child的MeasureSpec是EXACTLY的,直接獲取child的MeasureSpec,
- childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
- getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
- lp.leftMargin + lp.rightMargin,
- lp.width);
- }
- // 這里是對(duì)高做處理,與寬類似
- final int childHeightMeasureSpec;
- if (lp.height == LayoutParams.MATCH_PARENT) {
- final int height = Math.max(0, getMeasuredHeight()
- - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- - lp.topMargin - lp.bottomMargin);
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- height, MeasureSpec.EXACTLY);
- } else {
- childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
- getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
- lp.topMargin + lp.bottomMargin,
- lp.height);
- }
- //最終,再次測(cè)量child
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
- }
- }
總結(jié)
測(cè)量始于DecorView,通過不斷的遍歷子View的measure方法,根據(jù)ViewGroup的MeasureSpec及子View的LayoutParams來決定子View的MeasureSpec,進(jìn)一步獲取子View的測(cè)量寬高,然后逐層返回,不斷保存ViewGroup的測(cè)量寬高;
單一View,一般重寫此方法,針對(duì)wrap_content情況,規(guī)定View默認(rèn)的大小值,避免于match_parent情況一致。ViewGroup,若不重寫,就會(huì)執(zhí)行和單子View中相同邏輯,不會(huì)測(cè)量子View。一般會(huì)重寫onMeasure()方法,循環(huán)測(cè)量子View;