成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android進(jìn)階之深入理解View的測(cè)量(Measure)流程機(jī)制

移動(dòng)開發(fā) Android
View 的工作原理中最重要的就是測(cè)量、布局、繪制三大過程,而其中測(cè)量是最復(fù)雜的; 那么我們就來介紹下View 的測(cè)量過程;

[[424218]]

前言

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()方法中

  1. public static int getChildMeasureSpec( 
  2. int spec, int padding, int childDimension) { 
  3. //1、獲取parent的specMode 
  4.   int specMode = MeasureSpec.getMode(spec); 
  5. //2、獲取parent的specSize 
  6.   int specSize = MeasureSpec.getSize(spec); 
  7. //3、size=剩余的可用大小 
  8.   int size = Math.max(0, specSize - padding); 
  9.   int resultSize = 0; 
  10.   int resultMode = 0; 
  11.   //4、通過switch語句判斷parent的集中mode,分別處理 
  12.   switch (specMode) { 
  13.   // 5、parent為MeasureSpec.EXACTLY時(shí) 
  14.   case MeasureSpec.EXACTLY: 
  15.       if (childDimension >= 0) { 
  16.     //5.1、當(dāng)childDimension大于0時(shí),表示child的大小是 
  17.         //明確指出的,如layout_width= "100dp"
  18.           // 此時(shí)child的大小= childDimension, 
  19.           resultSize = childDimension; 
  20.           //child的測(cè)量模式= MeasureSpec.EXACTLY 
  21.           resultMode = MeasureSpec.EXACTLY; 
  22.       } else if (childDimension == LayoutParams.MATCH_PARENT) { 
  23.     //5.2、此時(shí)為L(zhǎng)ayoutParams.MATCH_PARENT 
  24.     //也就是    android:layout_width="match_parent" 
  25.       //因?yàn)閜arent的大小是明確的,child要匹配parent的大小 
  26.       //那么我們就直接讓child=parent的大小就好 
  27.           resultSize = size
  28.         //同樣,child的測(cè)量模式= MeasureSpec.EXACTLY 
  29.           resultMode = MeasureSpec.EXACTLY; 
  30.       } else if (childDimension == LayoutParams.WRAP_CONTENT) { 
  31.   //5.3、此時(shí)為L(zhǎng)ayoutParams.WRAP_CONTENT 
  32.     //也就是   android:layout_width="wrap_content"   
  33.     // 這個(gè)模式需要特別對(duì)待,child說我要的大小剛好夠放 
  34.     //需要展示的內(nèi)容就好,而此時(shí)我們并不知道child的內(nèi)容 
  35.     //需要多大的地方,暫時(shí)先把parent的size給他 
  36.           resultSize = size
  37.       //自然,child的mode就是MeasureSpec.AT_MOST的了 
  38.           resultMode = MeasureSpec.AT_MOST; 
  39.       } 
  40.       break; 
  41.   // 5、parent為AT_MOST,此時(shí)child最大不能超過parent 
  42.   case MeasureSpec.AT_MOST: 
  43.       if (childDimension >= 0) { 
  44.           //同樣child大小明確時(shí), 
  45.           //大小直接時(shí)指定的childDimension 
  46.           resultSize = childDimension; 
  47.           resultMode = MeasureSpec.EXACTLY; 
  48.       } else if (childDimension == LayoutParams.MATCH_PARENT) { 
  49.           // child要跟parent一樣大,resultSize=可用大小 
  50.           resultSize = size
  51.         //因?yàn)閜arent是AT_MOST,child的大小也還是未定的, 
  52.         //所以也是MeasureSpec.AT_MOST 
  53.           resultMode = MeasureSpec.AT_MOST; 
  54.       } else if (childDimension == LayoutParams.WRAP_CONTENT) { 
  55.           //又是特殊情況,先給child可用的大小 
  56.           resultSize = size
  57.           resultMode = MeasureSpec.AT_MOST; 
  58.       } 
  59.       break; 
  60.   // 這種模式是很少用的,我們也看下吧 
  61.   case MeasureSpec.UNSPECIFIED: 
  62.       if (childDimension >= 0) { 
  63.           // 與前面同樣的處理 
  64.           resultSize = childDimension; 
  65.           resultMode = MeasureSpec.EXACTLY; 
  66.       } else if (childDimension == LayoutParams.MATCH_PARENT) { 
  67.           // Child wants to be our size... find out how big it should 
  68.           // be 
  69.           resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size
  70.           resultMode = MeasureSpec.UNSPECIFIED; 
  71.       } else if (childDimension == LayoutParams.WRAP_CONTENT) { 
  72.           // Child wants to determine its own size.... find out how 
  73.           // big it should be 
  74.           resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size
  75.           resultMode = MeasureSpec.UNSPECIFIED; 
  76.       } 
  77.       break; 
  78.   } 
  79.   //通過傳入resultSize和resultMode生成一個(gè)MeasureSpec.返回 
  80.   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)鍵部分:

  1. private void performTraversals() { 
  2.       ... 
  3.   if (!mStopped) { 
  4.     //獲取頂層布局的childWidthMeasureSpec 
  5.       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);   
  6.     //獲取頂層布局的childHeightMeasureSpec 
  7.       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 
  8.       //測(cè)量開始測(cè)量 
  9.       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);        
  10.       } 
  11.   }  
  12.   if (didLayout) { 
  13.     //執(zhí)行布局方法 
  14.       performLayout(lp, desiredWindowWidth, desiredWindowHeight); 
  15.       ... 
  16.   } 
  17.   if (!cancelDraw && !newSurface) { 
  18.    ... 
  19.     //開始繪制了哦 
  20.           performDraw(); 
  21.       } 
  22.   }  

整個(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方法:

  1. private void performMeasure(int childWidthMeasureSpec,  
  2.   int childHeightMeasureSpec) { 
  3. Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 
  4. try { 
  5.   //mView其實(shí)就是我們的頂層DecorView,從DecorView開始測(cè)量 
  6.   mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
  7. } finally { 
  8.   Trace.traceEnd(Trace.TRACE_TAG_VIEW); 

2、measure()

源碼中找到了measure方法

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 
  2.         boolean optical = isLayoutModeOptical(this); 
  3.         if (optical != isLayoutModeOptical(mParent)) { 
  4.             Insets insets = getOpticalInsets(); 
  5.             int oWidth  = insets.left + insets.right
  6.             int oHeight = insets.top  + insets.bottom; 
  7.             widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth); 
  8.             heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); 
  9.         } 
  10.         // Suppress sign extension for the low bytes 
  11.         long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; 
  12.         if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); 
  13.         final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; 
  14.         // Optimize layout by avoiding an extra EXACTLY pass when the view is 
  15.         // already measured as the correct sizeIn API 23 and below, this 
  16.         // extra pass is required to make LinearLayout re-distribute weight. 
  17.         final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec 
  18.                 || heightMeasureSpec != mOldHeightMeasureSpec; 
  19.         final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY 
  20.                 && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; 
  21.         final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) 
  22.                 && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); 
  23.         final boolean needsLayout = specChanged 
  24.                 && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize); 
  25.         if (forceLayout || needsLayout) { 
  26.             // first clears the measured dimension flag 
  27.             mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; 
  28.             resolveRtlPropertiesIfNeeded(); 
  29.             int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); 
  30.             if (cacheIndex < 0 || sIgnoreMeasureCache) { 
  31.                 // measure ourselves, this should set the measured dimension flag back 
  32.                 onMeasure(widthMeasureSpec, heightMeasureSpec); 
  33.                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; 
  34.             } else { 
  35.                 long value = mMeasureCache.valueAt(cacheIndex); 
  36.                 // Casting a long to int drops the high 32 bits, no mask needed 
  37.                 setMeasuredDimensionRaw((int) (value >> 32), (int) value); 
  38.                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; 
  39.             } 
  40.             // flag not set, setMeasuredDimension() was not invoked, we raise 
  41.             // an exception to warn the developer 
  42.             if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { 
  43.                 throw new IllegalStateException("View with id " + getId() + ": " 
  44.                         + getClass().getName() + "#onMeasure() did not set the" 
  45.                         + " measured dimension by calling" 
  46.                         + " setMeasuredDimension()"); 
  47.             } 
  48.             mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; 
  49.         } 
  50.         mOldWidthMeasureSpec = widthMeasureSpec; 
  51.         mOldHeightMeasureSpec = heightMeasureSpec; 
  52.         mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | 
  53.                 (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension 
  54.     } 

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寬/高;

  1. protected void onMeasure(int widthMeasureSpec,  
  2. int heightMeasureSpec) { 
  3.  setMeasuredDimension( 
  4.     getDefaultSize(getSuggestedMinimumWidth(), 
  5.                                 widthMeasureSpec), 
  6.     getDefaultSize(getSuggestedMinimumHeight(), 
  7.                                  heightMeasureSpec)); 
  8. getSuggestedMinimumWidth() 
  9. protected int getSuggestedMinimumWidth() { 
  10.   return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); 
  11.   } 
  12.  前View是否有背景?沒有就返回android:minWidth設(shè)置的值:有就返回android:minWidth和mBackground.getMinimumWidth()中較大的那個(gè)值; 
  13. public int getMinimumWidth() { 
  14.     final int intrinsicWidth = getIntrinsicWidth(); 
  15.     //返回背景圖Drawable的原始寬度 
  16.     return intrinsicWidth > 0 ? intrinsicWidth :0 ; 

getIntrinsicWidth()獲取的是背景圖的原始寬度,背景圖是BitmapDrawable則有原始寬度,在沒有原始寬度的情況下則返回0;

4、getDefaultSize()

  1. public static int getDefaultSize(int sizeint measureSpec) { 
  2.   int result = size
  3.  //1、獲得MeasureSpec的mode 
  4.   int specMode = MeasureSpec.getMode(measureSpec); 
  5.  //2、獲得MeasureSpec的specSize 
  6.   int specSize = MeasureSpec.getSize(measureSpec); 
  7.   switch (specMode) { 
  8.   case MeasureSpec.UNSPECIFIED: 
  9.     //這個(gè)我們先不看他 
  10.       result = size
  11.       break; 
  12.   case MeasureSpec.AT_MOST: 
  13.   case MeasureSpec.EXACTLY: 
  14.   //3、可以看到,最終返回的size就是我們MeasureSpec中測(cè)量得到的size 
  15.       result = specSize; 
  16.       break; 
  17.   } 
  18.   return result; 

測(cè)量模式是AT_MOST還是EXACTLY,最終返回的Size是一樣的;

5、setMeasureDimension

該方法是儲(chǔ)存測(cè)量后的View的寬和高的,在自定義View的時(shí)候,我們自己重寫的onMeasure方法最后一定要調(diào)用這個(gè)方法,否則會(huì)報(bào)錯(cuò)

  1. protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 
  2.     //1、判斷是否使用視覺邊界布局 
  3.   boolean optical = isLayoutModeOptical(this); 
  4.   //2、判斷view和parentView使用的視覺邊界布局是否一致 
  5.   if (optical != isLayoutModeOptical(mParent)) { 
  6.       //不一致時(shí)要做一些邊界的處理 
  7.       Insets insets = getOpticalInsets(); 
  8.       int opticalWidth  = insets.left + insets.right
  9.       int opticalHeight = insets.top  + insets.bottom; 
  10.       measuredWidth  += optical ? opticalWidth  : -opticalWidth; 
  11.       measuredHeight += optical ? opticalHeight : -opticalHeight; 
  12.   } 
  13.   //3、重點(diǎn)來了,經(jīng)過過濾之后調(diào)用了setMeasuredDimensionRaw方法,看來應(yīng)該是這個(gè)方法設(shè)置我們的view的大小 
  14.   setMeasuredDimensionRaw(measuredWidth, measuredHeight); 
  15. setMeasureDimensionRaw方法: 
  16. private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { 
  17.   //最終將測(cè)量好的大小存儲(chǔ)到mMeasuredWidth和mMeasuredHeight上,所以在測(cè)量之后 
  18.   //我們可以通過調(diào)用getMeasuredWidth獲得測(cè)量的寬、getMeasuredHeight獲得高 
  19.   mMeasuredWidth = measuredWidth; 
  20.   mMeasuredHeight = measuredHeight; 
  21.   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è)量

  1. protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 
  2. //參數(shù)說明:父視圖的測(cè)量規(guī)格(MeasureSpec) 
  3.         final int size = mChildrenCount; 
  4.         final View[] children = mChildren; 
  5.         //遍歷所有的子view 
  6.         for (int i = 0; i < size; ++i) { 
  7.             final View child = children[i]; 
  8.         //如果View的狀態(tài)不是GONE就調(diào)用measureChild()去進(jìn)行下一步的測(cè)量 
  9.             if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 
  10.                 measureChild(child, widthMeasureSpec, heightMeasureSpec); 
  11.             } 
  12.         } 
  13.     } 

2、measureChild()

作用就是計(jì)算單個(gè)子View的MeasureSpec,調(diào)用子View的measure進(jìn)行每個(gè)子View最后的寬、高的測(cè)量

  1. protected void measureChild(View child, int parentWidthMeasureSpec, 
  2.             int parentHeightMeasureSpec) { 
  3.         // 獲取子視圖的布局參數(shù) 
  4.         final LayoutParams lp = child.getLayoutParams(); 
  5.         // 調(diào)用getChildMeasureSpec(),根據(jù)父視圖的MeasureSpec & 布局參數(shù)LayoutParams,計(jì)算單個(gè)子View的MeasureSpec 
  6.          // getChildMeasureSpec()請(qǐng)回看上面的解析 
  7.          // 獲取 ChildView 的 widthMeasureSpec 
  8.         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 
  9.                 mPaddingLeft + mPaddingRight, lp.width); 
  10.         // 獲取 ChildView 的 heightMeasureSpec 
  11.         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 
  12.                 mPaddingTop + mPaddingBottom, lp.height); 
  13.         // 將計(jì)算好的子View的MeasureSpec值傳入measure(),進(jìn)行最后的測(cè)量 
  14.         child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
  15.     } 

3、measure()

和View的measure一致

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 
  2.     ... 
  3.     int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : 
  4.             mMeasureCache.indexOfKey(key); 
  5.     if (cacheIndex < 0 || sIgnoreMeasureCache) { 
  6.         // 調(diào)用onMeasure()計(jì)算視圖大小 
  7.         onMeasure(widthMeasureSpec, heightMeasureSpec); 
  8.         mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; 
  9.     } else { 
  10.         ... 

4、XXXLayout.onMeasure()

  1. 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) 
  2. FrameLayout為例,看看它的onMeasure是如何實(shí)現(xiàn)的: 
  3. //這里的widthMeasureSpec、heightMeasureSpec 
  4. //其實(shí)就是我們frameLayout可用的widthMeasureSpec 、 
  5. //heightMeasureSpec 
  6. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  7.   //1、獲得frameLayout下childView的個(gè)數(shù) 
  8.   int count = getChildCount(); 
  9. //2、看這里的代碼我們可以根據(jù)前面的Measure圖來進(jìn)行分析,因?yàn)橹灰猵arent 
  10. //不是EXACTLY模式,以frameLayout為例,假設(shè)frameLayout本身還不是EXACTL模式, 
  11.  // 那么表示他的大小此時(shí)還是不確定的,從表得知,此時(shí)frameLayout的大小是根據(jù) 
  12.  //childView的最大值來設(shè)置的,這樣就很好理解了,也就是childView測(cè)量好后還要再 
  13. //測(cè)量一次,因?yàn)榇藭r(shí)frameLayout的值已經(jīng)可以算出來了,對(duì)于child為MATCH_PARENT 
  14. //的,child的大小也就確定了,理解了這里,后面的代碼就很 容易看懂了 
  15.   final boolean measureMatchParentChildren = 
  16.           MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || 
  17.           MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; 
  18.    //3、清理存儲(chǔ)模式為MATCH_PARENT的child的隊(duì)列 
  19.   mMatchParentChildren.clear(); 
  20.   //4、下面三個(gè)值最終會(huì)用來設(shè)置frameLayout的大小 
  21.   int maxHeight = 0; 
  22.   int maxWidth = 0; 
  23.   int childState = 0; 
  24.   //5、開始便利frameLayout下的所有child 
  25.   for (int i = 0; i < count; i++) { 
  26.       final View child = getChildAt(i); 
  27.       //6、小發(fā)現(xiàn)哦,只要mMeasureAllChildren是true,就算child是GONE也會(huì)被測(cè)量哦, 
  28.       if (mMeasureAllChildren || child.getVisibility() != GONE) { 
  29.           //7、開始測(cè)量childView  
  30.           measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 
  31.           //8、下面代碼是獲取child中的width 和height的最大值,后面用來重新設(shè)置frameLayout,有需要的話 
  32.           final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 
  33.           maxWidth = Math.max(maxWidth, 
  34.                   child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 
  35.           maxHeight = Math.max(maxHeight, 
  36.                   child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 
  37.           childState = combineMeasuredStates(childState, child.getMeasuredState()); 
  38.         //9、如果frameLayout不是EXACTLY, 
  39.           if (measureMatchParentChildren) { 
  40.               if (lp.width == LayoutParams.MATCH_PARENT || 
  41.                       lp.height == LayoutParams.MATCH_PARENT) { 
  42. //10、存儲(chǔ)LayoutParams.MATCH_PARENT的child,因?yàn)楝F(xiàn)在還不知道frameLayout大小, 
  43. //也就無法設(shè)置child的大小,后面需重新測(cè)量 
  44.                   mMatchParentChildren.add(child); 
  45.               } 
  46.           } 
  47.       } 
  48.   } 
  49.     .... 
  50.   //11、這里開始設(shè)置frameLayout的大小 
  51.   setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 
  52.           resolveSizeAndState(maxHeight, heightMeasureSpec, 
  53.                   childState << MEASURED_HEIGHT_STATE_SHIFT)); 
  54. //12、frameLayout大小確認(rèn)了,我們就需要對(duì)寬或高為L(zhǎng)ayoutParams.MATCH_PARENTchild重新測(cè)量,設(shè)置大小 
  55.   count = mMatchParentChildren.size(); 
  56.   if (count > 1) { 
  57.       for (int i = 0; i < count; i++) { 
  58.           final View child = mMatchParentChildren.get(i); 
  59.           final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 
  60.           final int childWidthMeasureSpec; 
  61.           if (lp.width == LayoutParams.MATCH_PARENT) { 
  62.               final int width = Math.max(0, getMeasuredWidth() 
  63.                       - getPaddingLeftWithForeground() - getPaddingRightWithForeground() 
  64.                       - lp.leftMargin - lp.rightMargin); 
  65.   //13、注意這里,為child是EXACTLY類型的childWidthMeasureSpec, 
  66.   //也就是大小已經(jīng)測(cè)量出來了不需要再測(cè)量了 
  67.   //通過MeasureSpec.makeMeasureSpec生成相應(yīng)的MeasureSpec 
  68.               childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 
  69.                       width, MeasureSpec.EXACTLY); 
  70.           } else { 
  71.   //14、如果不是,說明此時(shí)的child的MeasureSpec是EXACTLY的,直接獲取child的MeasureSpec, 
  72.               childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 
  73.                       getPaddingLeftWithForeground() + getPaddingRightWithForeground() + 
  74.                       lp.leftMargin + lp.rightMargin, 
  75.                       lp.width); 
  76.           } 
  77.   // 這里是對(duì)高做處理,與寬類似 
  78.           final int childHeightMeasureSpec; 
  79.           if (lp.height == LayoutParams.MATCH_PARENT) { 
  80.               final int height = Math.max(0, getMeasuredHeight() 
  81.                       - getPaddingTopWithForeground() - getPaddingBottomWithForeground() 
  82.                       - lp.topMargin - lp.bottomMargin); 
  83.               childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 
  84.                       height, MeasureSpec.EXACTLY); 
  85.           } else { 
  86.               childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 
  87.                       getPaddingTopWithForeground() + getPaddingBottomWithForeground() + 
  88.                       lp.topMargin + lp.bottomMargin, 
  89.                       lp.height); 
  90.           } 
  91.   //最終,再次測(cè)量child 
  92.           child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
  93.       } 
  94.   } 

總結(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;

 

責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2021-09-30 07:36:51

AndroidViewDraw

2021-09-17 06:55:50

AndroidLayoutView

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-09-15 07:31:33

Android窗口管理

2021-09-24 08:10:40

Java 語言 Java 基礎(chǔ)

2021-09-10 07:31:54

AndroidAppStartup原理

2017-05-03 17:00:16

Android渲染機(jī)制

2021-09-18 06:56:01

JavaCAS機(jī)制

2021-09-08 06:51:52

AndroidRetrofit原理

2024-12-30 08:02:40

2022-10-11 07:43:34

AndroidSyncGradle 構(gòu)建

2021-08-24 07:53:28

AndroidActivity生命周期

2017-01-13 22:42:15

iosswift

2014-07-15 17:17:31

AdapterAndroid

2021-02-17 11:25:33

前端JavaScriptthis

2017-08-08 09:15:41

前端JavaScript頁(yè)面渲染

2017-07-12 14:58:21

AndroidInstant Run

2024-06-06 09:58:13

2023-10-13 13:30:00

MySQL鎖機(jī)制

2011-07-18 14:38:44

子查詢外部查詢
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲成人在线视频播放 | 久久99精品久久久久久狂牛 | 亚洲精品亚洲人成人网 | 国产日韩91 | 91久久国产精品 | 精品蜜桃一区二区三区 | 美女毛片免费看 | 午夜色播| 综合视频在线 | 国产一级在线 | a成人| 婷婷二区 | 精品国产乱码久久久久久1区2区 | 精品国产伦一区二区三区观看说明 | 久久久久久亚洲精品 | 亚洲精品在线国产 | 国产精品久久久精品 | 亚洲精品在线观 | 成人在线播放网址 | 亚洲日本视频 | 91在线资源 | 国产精品久久久久久久久久久免费看 | 日本二区 | 欧美日韩一区精品 | 日韩毛片网 | 欧美一区视频 | 99精品视频免费观看 | 在线国产中文字幕 | 色毛片 | 欧美精品一区二区三区视频 | 青青草久久 | 精品九九 | 波多野结衣中文字幕一区二区三区 | 欧美一级在线 | 日本三级电影在线免费观看 | 一级全黄视频 | 久久久久9999亚洲精品 | 久久久久久久电影 | 久久av一区二区三区 | 一区在线观看 | 91精品在线看 |