Android进阶之深入理解View的测量(Measure)流程机制

时间:2022-09-20 14:33:30

Android进阶之深入理解View的测量(Measure)流程机制

前言

View 的工作原理中最重要的就是测量、布局、绘制三大过程,而其中测量是最复杂的;

那么我们就来介绍下View 的测量过程;

一、MeasureSpec

测量自身的大小的时候,会执行measure(int widthMeasureSpec, int heightMeasureSpec)方法。注意方法中两个参数,它们其实是一个int 类型的MeasureSpec;

1、specMode

测量模式分为三种:

  • UNSPECIFIED模式:本质就是不限制模式,父视图不对子View进行任何约束,View想要多大要多大,想要多长要多长;
  • EXACTLY模式:该模式其实对应的场景就是match_parent或者是一个具体的数据(50dp或80px),父视图为子View指定一个确切的大小,无论子View的值设置多大,都不能超出父视图的范围;
  • AT_MOST模式:这个模式对应的场景就是wrap_content,其内容就是父视图给子View设置一个最大尺寸,子View只要不超过这个尺寸即可;

2、MeasureSpec

View的MeasureSpec值是根据子View的布局参数(LayoutParams)和父容器的MeasureSpec至计算而来的,其具体逻辑封装在了getChildMeasureSpec()方法中

  1. publicstaticintgetChildMeasureSpec(
  2. intspec,intpadding,intchildDimension){
  3. //1、获取parent的specMode
  4. intspecMode=MeasureSpec.getMode(spec);
  5. //2、获取parent的specSize
  6. intspecSize=MeasureSpec.getSize(spec);
  7. //3、size=剩余的可用大小
  8. intsize=Math.max(0,specSize-padding);
  9. intresultSize=0;
  10. intresultMode=0;
  11. //4、通过switch语句判断parent的集中mode,分别处理
  12. switch(specMode){
  13. //5、parent为MeasureSpec.EXACTLY时
  14. caseMeasureSpec.EXACTLY:
  15. if(childDimension>=0){
  16. //5.1、当childDimension大于0时,表示child的大小是
  17. //明确指出的,如layout_width="100dp";
  18. //此时child的大小=childDimension,
  19. resultSize=childDimension;
  20. //child的测量模式=MeasureSpec.EXACTLY
  21. resultMode=MeasureSpec.EXACTLY;
  22. }elseif(childDimension==LayoutParams.MATCH_PARENT){
  23. //5.2、此时为LayoutParams.MATCH_PARENT
  24. //也就是android:layout_width="match_parent"
  25. //因为parent的大小是明确的,child要匹配parent的大小
  26. //那么我们就直接让child=parent的大小就好
  27. resultSize=size;
  28. //同样,child的测量模式=MeasureSpec.EXACTLY
  29. resultMode=MeasureSpec.EXACTLY;
  30. }elseif(childDimension==LayoutParams.WRAP_CONTENT){
  31. //5.3、此时为LayoutParams.WRAP_CONTENT
  32. //也就是android:layout_width="wrap_content"
  33. //这个模式需要特别对待,child说我要的大小刚好够放
  34. //需要展示的内容就好,而此时我们并不知道child的内容
  35. //需要多大的地方,暂时先把parent的size给他
  36. resultSize=size;
  37. //自然,child的mode就是MeasureSpec.AT_MOST的了
  38. resultMode=MeasureSpec.AT_MOST;
  39. }
  40. break;
  41. //5、parent为AT_MOST,此时child最大不能超过parent
  42. caseMeasureSpec.AT_MOST:
  43. if(childDimension>=0){
  44. //同样child大小明确时,
  45. //大小直接时指定的childDimension
  46. resultSize=childDimension;
  47. resultMode=MeasureSpec.EXACTLY;
  48. }elseif(childDimension==LayoutParams.MATCH_PARENT){
  49. //child要跟parent一样大,resultSize=可用大小
  50. resultSize=size;
  51. //因为parent是AT_MOST,child的大小也还是未定的,
  52. //所以也是MeasureSpec.AT_MOST
  53. resultMode=MeasureSpec.AT_MOST;
  54. }elseif(childDimension==LayoutParams.WRAP_CONTENT){
  55. //又是特殊情况,先给child可用的大小
  56. resultSize=size;
  57. resultMode=MeasureSpec.AT_MOST;
  58. }
  59. break;
  60. //这种模式是很少用的,我们也看下吧
  61. caseMeasureSpec.UNSPECIFIED:
  62. if(childDimension>=0){
  63. //与前面同样的处理
  64. resultSize=childDimension;
  65. resultMode=MeasureSpec.EXACTLY;
  66. }elseif(childDimension==LayoutParams.MATCH_PARENT){
  67. //Childwantstobeoursize...findouthowbigitshould
  68. //be
  69. resultSize=View.sUseZeroUnspecifiedMeasureSpec?0:size;
  70. resultMode=MeasureSpec.UNSPECIFIED;
  71. }elseif(childDimension==LayoutParams.WRAP_CONTENT){
  72. //Childwantstodetermineitsownsize....findouthow
  73. //bigitshouldbe
  74. resultSize=View.sUseZeroUnspecifiedMeasureSpec?0:size;
  75. resultMode=MeasureSpec.UNSPECIFIED;
  76. }
  77. break;
  78. }
  79. //通过传入resultSize和resultMode生成一个MeasureSpec.返回
  80. returnMeasureSpec.makeMeasureSpec(resultSize,resultMode);
  81. }
  • ①当子View采用具体数值(dp / px)时:无论父容器的测量模式是什么,子View的测量模式都是EXACTLY且大小等于设置的具体数值;
  • ②当子View采用match_parent时:子View的测量模式与父容器的测量模式一致;若测量模式为EXACTLY,则子View的大小为父容器的剩余空间;若测量模式为AT_MOST,则子View的大小不超过父容器的剩余空间;
  • ③当子View采用wrap_parent时:如果父容器测量模式为UNSPECIFIED,子View也为UNSPECIFIED,否则子View为AT_MOST且大小不超过父容器的剩余空间;

Android进阶之深入理解View的测量(Measure)流程机制

二、view测量过程

Android进阶之深入理解View的测量(Measure)流程机制

1、performMeasure()

加载好系统布局资源后,会触发ViewRootImpl的performTraversals()方法,该方法内容会开始执行测量、布局和绘制的工作,我们来看这个方法的源码关键部分:

  1. privatevoidperformTraversals(){
  2. ...
  3. if(!mStopped){
  4. //获取顶层布局的childWidthMeasureSpec
  5. intchildWidthMeasureSpec=getRootMeasureSpec(mWidth,lp.width);
  6. //获取顶层布局的childHeightMeasureSpec
  7. intchildHeightMeasureSpec=getRootMeasureSpec(mHeight,lp.height);
  8. //测量开始测量
  9. performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
  10. }
  11. }
  12. if(didLayout){
  13. //执行布局方法
  14. performLayout(lp,desiredWindowWidth,desiredWindowHeight);
  15. ...
  16. }
  17. if(!cancelDraw&&!newSurface){
  18. ...
  19. //开始绘制了哦
  20. performDraw();
  21. }
  22. }
  23. }

整个方法内部其实就是做了一些基础的判断后,再顺序的调用测量、布局和绘制的相关方法,从而完成自定义View的整个工作流程;

performTraversals方法,使用的是getRootMeasureSpec方法来获取子View的MeasureSpec;

整个Activity的顶层View其实就是一个DecorView,所以这里获取的其实是DeorView的MeasureSpec,然后将其传入performMeasure方法中去开始测量,现在看看PerformMeasure方法:

  1. privatevoidperformMeasure(intchildWidthMeasureSpec,
  2. intchildHeightMeasureSpec){
  3. Trace.traceBegin(Trace.TRACE_TAG_VIEW,"measure");
  4. try{
  5. //mView其实就是我们的顶层DecorView,从DecorView开始测量
  6. mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
  7. }finally{
  8. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  9. }
  10. }

2、measure()

源码中找到了measure方法

  1. publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){
  2. booleanoptical=isLayoutModeOptical(this);
  3. if(optical!=isLayoutModeOptical(mParent)){
  4. Insetsinsets=getOpticalInsets();
  5. intoWidth=insets.left+insets.right;
  6. intoHeight=insets.top+insets.bottom;
  7. widthMeasureSpec=MeasureSpec.adjust(widthMeasureSpec,optical?-oWidth:oWidth);
  8. heightMeasureSpec=MeasureSpec.adjust(heightMeasureSpec,optical?-oHeight:oHeight);
  9. }
  10. //Suppresssignextensionforthelowbytes
  11. longkey=(long)widthMeasureSpec<<32|(long)heightMeasureSpec&0xffffffffL;
  12. if(mMeasureCache==null)mMeasureCache=newLongSparseLongArray(2);
  13. finalbooleanforceLayout=(mPrivateFlags&PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT;
  14. //OptimizelayoutbyavoidinganextraEXACTLYpasswhentheviewis
  15. //alreadymeasuredasthecorrectsize.InAPI23andbelow,this
  16. //extrapassisrequiredtomakeLinearLayoutre-distributeweight.
  17. finalbooleanspecChanged=widthMeasureSpec!=mOldWidthMeasureSpec
  18. ||heightMeasureSpec!=mOldHeightMeasureSpec;
  19. finalbooleanisSpecExactly=MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.EXACTLY
  20. &&MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.EXACTLY;
  21. finalbooleanmatchesSpecSize=getMeasuredWidth()==MeasureSpec.getSize(widthMeasureSpec)
  22. &&getMeasuredHeight()==MeasureSpec.getSize(heightMeasureSpec);
  23. finalbooleanneedsLayout=specChanged
  24. &&(sAlwaysRemeasureExactly||!isSpecExactly||!matchesSpecSize);
  25. if(forceLayout||needsLayout){
  26. //firstclearsthemeasureddimensionflag
  27. mPrivateFlags&=~PFLAG_MEASURED_DIMENSION_SET;
  28. resolveRtlPropertiesIfNeeded();
  29. intcacheIndex=forceLayout?-1:mMeasureCache.indexOfKey(key);
  30. if(cacheIndex<0||sIgnoreMeasureCache){
  31. //measureourselves,thisshouldsetthemeasureddimensionflagback
  32. onMeasure(widthMeasureSpec,heightMeasureSpec);
  33. mPrivateFlags3&=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  34. }else{
  35. longvalue=mMeasureCache.valueAt(cacheIndex);
  36. //Castingalongtointdropsthehigh32bits,nomaskneeded
  37. setMeasuredDimensionRaw((int)(value>>32),(int)value);
  38. mPrivateFlags3|=PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  39. }
  40. //flagnotset,setMeasuredDimension()wasnotinvoked,weraise
  41. //anexceptiontowarnthedeveloper
  42. if((mPrivateFlags&PFLAG_MEASURED_DIMENSION_SET)!=PFLAG_MEASURED_DIMENSION_SET){
  43. thrownewIllegalStateException("Viewwithid"+getId()+":"
  44. +getClass().getName()+"#onMeasure()didnotsetthe"
  45. +"measureddimensionbycalling"
  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);//suppresssignextension
  54. }

View的测量是View.onMeasure而ViewGroup的测量则是XXLayout.onMeasure,这两种onMeasure方法的实现是不同的

3、View.onMeasure()

获取一个建议最小值;

调用getDefaultSize方法定义对View尺寸的测量逻辑;

调用setMeasureDimension()储存测量后的View宽/高;

  1. protectedvoidonMeasure(intwidthMeasureSpec,
  2. intheightMeasureSpec){
  3. setMeasuredDimension(
  4. getDefaultSize(getSuggestedMinimumWidth(),
  5. widthMeasureSpec),
  6. getDefaultSize(getSuggestedMinimumHeight(),
  7. heightMeasureSpec));
  8. }
  9. getSuggestedMinimumWidth()
  10. protectedintgetSuggestedMinimumWidth(){
  11. return(mBackground==null)?mMinWidth:max(mMinWidth,mBackground.getMinimumWidth());
  12. }
  13. View是否有背景?没有就返回android:minWidth设置的值:有就返回android:minWidth和mBackground.getMinimumWidth()中较大的那个值;
  14. publicintgetMinimumWidth(){
  15. finalintintrinsicWidth=getIntrinsicWidth();
  16. //返回背景图Drawable的原始宽度
  17. returnintrinsicWidth>0?intrinsicWidth:0;
  18. }

getIntrinsicWidth()获取的是背景图的原始宽度,背景图是BitmapDrawable则有原始宽度,在没有原始宽度的情况下则返回0;

4、getDefaultSize()

  1. publicstaticintgetDefaultSize(intsize,intmeasureSpec){
  2. intresult=size;
  3. //1、获得MeasureSpec的mode
  4. intspecMode=MeasureSpec.getMode(measureSpec);
  5. //2、获得MeasureSpec的specSize
  6. intspecSize=MeasureSpec.getSize(measureSpec);
  7. switch(specMode){
  8. caseMeasureSpec.UNSPECIFIED:
  9. //这个我们先不看他
  10. result=size;
  11. break;
  12. caseMeasureSpec.AT_MOST:
  13. caseMeasureSpec.EXACTLY:
  14. //3、可以看到,最终返回的size就是我们MeasureSpec中测量得到的size
  15. result=specSize;
  16. break;
  17. }
  18. returnresult;
  19. }

测量模式是AT_MOST还是EXACTLY,最终返回的Size是一样的;

5、setMeasureDimension

该方法是储存测量后的View的宽和高的,在自定义View的时候,我们自己重写的onMeasure方法最后一定要调用这个方法,否则会报错

  1. protectedfinalvoidsetMeasuredDimension(intmeasuredWidth,intmeasuredHeight){
  2. //1、判断是否使用视觉边界布局
  3. booleanoptical=isLayoutModeOptical(this);
  4. //2、判断view和parentView使用的视觉边界布局是否一致
  5. if(optical!=isLayoutModeOptical(mParent)){
  6. //不一致时要做一些边界的处理
  7. Insetsinsets=getOpticalInsets();
  8. intopticalWidth=insets.left+insets.right;
  9. intopticalHeight=insets.top+insets.bottom;
  10. measuredWidth+=optical?opticalWidth:-opticalWidth;
  11. measuredHeight+=optical?opticalHeight:-opticalHeight;
  12. }
  13. //3、重点来了,经过过滤之后调用了setMeasuredDimensionRaw方法,看来应该是这个方法设置我们的view的大小
  14. setMeasuredDimensionRaw(measuredWidth,measuredHeight);
  15. }
  16. setMeasureDimensionRaw方法:
  17. privatevoidsetMeasuredDimensionRaw(intmeasuredWidth,intmeasuredHeight){
  18. //最终将测量好的大小存储到mMeasuredWidth和mMeasuredHeight上,所以在测量之后
  19. //我们可以通过调用getMeasuredWidth获得测量的宽、getMeasuredHeight获得高
  20. mMeasuredWidth=measuredWidth;
  21. mMeasuredHeight=measuredHeight;
  22. mPrivateFlags|=PFLAG_MEASURED_DIMENSION_SET;
  23. }

以上就是View的测量过程,其顺序为:

performTraversals->performMeasure->measure->onMeasure-> setMeasuredDimension-> setMeasuredDimensionRaw,由setMeasuredDimensionRaw最终保存测量的数据

三、ViewGroup的测量过程详解

Android进阶之深入理解View的测量(Measure)流程机制

1、measureChildren()

作用就是遍历子View并调用measureChild()进行下一步测量

  1. protectedvoidmeasureChildren(intwidthMeasureSpec,intheightMeasureSpec){
  2. //参数说明:父视图的测量规格(MeasureSpec)
  3. finalintsize=mChildrenCount;
  4. finalView[]children=mChildren;
  5. //遍历所有的子view
  6. for(inti=0;i<size;++i){
  7. finalViewchild=children[i];
  8. //如果View的状态不是GONE就调用measureChild()去进行下一步的测量
  9. if((child.mViewFlags&VISIBILITY_MASK)!=GONE){
  10. measureChild(child,widthMeasureSpec,heightMeasureSpec);
  11. }
  12. }
  13. }

2、measureChild()

作用就是计算单个子View的MeasureSpec,调用子View的measure进行每个子View最后的宽、高的测量

  1. protectedvoidmeasureChild(Viewchild,intparentWidthMeasureSpec,
  2. intparentHeightMeasureSpec){
  3. //获取子视图的布局参数
  4. finalLayoutParamslp=child.getLayoutParams();
  5. //调用getChildMeasureSpec(),根据父视图的MeasureSpec&布局参数LayoutParams,计算单个子View的MeasureSpec
  6. //getChildMeasureSpec()请回看上面的解析
  7. //获取ChildView的widthMeasureSpec
  8. finalintchildWidthMeasureSpec=getChildMeasureSpec(parentWidthMeasureSpec,
  9. mPaddingLeft+mPaddingRight,lp.width);
  10. //获取ChildView的heightMeasureSpec
  11. finalintchildHeightMeasureSpec=getChildMeasureSpec(parentHeightMeasureSpec,
  12. mPaddingTop+mPaddingBottom,lp.height);
  13. //将计算好的子View的MeasureSpec值传入measure(),进行最后的测量
  14. child.measure(childWidthMeasureSpec,childHeightMeasureSpec);
  15. }

3、measure()

和View的measure一致

  1. publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){
  2. ...
  3. intcacheIndex=(mPrivateFlags&PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT?-1:
  4. mMeasureCache.indexOfKey(key);
  5. if(cacheIndex<0||sIgnoreMeasureCache){
  6. //调用onMeasure()计算视图大小
  7. onMeasure(widthMeasureSpec,heightMeasureSpec);
  8. mPrivateFlags3&=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  9. }else{
  10. ...
  11. }

4、XXXLayout.onMeasure()

  1. ViewGroup的onMeasure和View的onMeasure是不同的,究其原因其实是因为ViewGroup是一个抽象类,所以即便它继承了View也不用必须实现View中的onMeasure方法,而它的子类不具备通用的布局特性,这导致他们的子View的测量方法各不相同,因此,ViewGroup无法对onMeasure()做统一的实现
  2. FrameLayout为例,看看它的onMeasure是如何实现的:
  3. //这里的widthMeasureSpec、heightMeasureSpec
  4. //其实就是我们frameLayout可用的widthMeasureSpec、
  5. //heightMeasureSpec
  6. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
  7. //1、获得frameLayout下childView的个数
  8. intcount=getChildCount();
  9. //2、看这里的代码我们可以根据前面的Measure图来进行分析,因为只要parent
  10. //不是EXACTLY模式,以frameLayout为例,假设frameLayout本身还不是EXACTL模式,
  11. //那么表示他的大小此时还是不确定的,从表得知,此时frameLayout的大小是根据
  12. //childView的最大值来设置的,这样就很好理解了,也就是childView测量好后还要再
  13. //测量一次,因为此时frameLayout的值已经可以算出来了,对于child为MATCH_PARENT
  14. //的,child的大小也就确定了,理解了这里,后面的代码就很容易看懂了
  15. finalbooleanmeasureMatchParentChildren=
  16. MeasureSpec.getMode(widthMeasureSpec)!=MeasureSpec.EXACTLY||
  17. MeasureSpec.getMode(heightMeasureSpec)!=MeasureSpec.EXACTLY;
  18. //3、清理存储模式为MATCH_PARENT的child的队列
  19. mMatchParentChildren.clear();
  20. //4、下面三个值最终会用来设置frameLayout的大小
  21. intmaxHeight=0;
  22. intmaxWidth=0;
  23. intchildState=0;
  24. //5、开始便利frameLayout下的所有child
  25. for(inti=0;i<count;i++){
  26. finalViewchild=getChildAt(i);
  27. //6、小发现哦,只要mMeasureAllChildren是true,就算child是GONE也会被测量哦,
  28. if(mMeasureAllChildren||child.getVisibility()!=GONE){
  29. //7、开始测量childView
  30. measureChildWithMargins(child,widthMeasureSpec,0,heightMeasureSpec,0);
  31. //8、下面代码是获取child中的width和height的最大值,后面用来重新设置frameLayout,有需要的话
  32. finalLayoutParamslp=(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、存储LayoutParams.MATCH_PARENT的child,因为现在还不知道frameLayout大小,
  43. //也就无法设置child的大小,后面需重新测量
  44. mMatchParentChildren.add(child);
  45. }
  46. }
  47. }
  48. }
  49. ....
  50. //11、这里开始设置frameLayout的大小
  51. setMeasuredDimension(resolveSizeAndState(maxWidth,widthMeasureSpec,childState),
  52. resolveSizeAndState(maxHeight,heightMeasureSpec,
  53. childState<));
  54. //12、frameLayout大小确认了,我们就需要对宽或高为LayoutParams.MATCH_PARENTchild重新测量,设置大小
  55. count=mMatchParentChildren.size();
  56. if(count>1){
  57. for(inti=0;i<count;i++){
  58. finalViewchild=mMatchParentChildren.get(i);
  59. finalMarginLayoutParamslp=(MarginLayoutParams)child.getLayoutParams();
  60. finalintchildWidthMeasureSpec;
  61. if(lp.width==LayoutParams.MATCH_PARENT){
  62. finalintwidth=Math.max(0,getMeasuredWidth()
  63. -getPaddingLeftWithForeground()-getPaddingRightWithForeground()
  64. -lp.leftMargin-lp.rightMargin);
  65. //13、注意这里,为child是EXACTLY类型的childWidthMeasureSpec,
  66. //也就是大小已经测量出来了不需要再测量了
  67. //通过MeasureSpec.makeMeasureSpec生成相应的MeasureSpec
  68. childWidthMeasureSpec=MeasureSpec.makeMeasureSpec(
  69. width,MeasureSpec.EXACTLY);
  70. }else{
  71. //14、如果不是,说明此时的child的MeasureSpec是EXACTLY的,直接获取child的MeasureSpec,
  72. childWidthMeasureSpec=getChildMeasureSpec(widthMeasureSpec,
  73. getPaddingLeftWithForeground()+getPaddingRightWithForeground()+
  74. lp.leftMargin+lp.rightMargin,
  75. lp.width);
  76. }
  77. //这里是对高做处理,与宽类似
  78. finalintchildHeightMeasureSpec;
  79. if(lp.height==LayoutParams.MATCH_PARENT){
  80. finalintheight=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. //最终,再次测量child
  92. child.measure(childWidthMeasureSpec,childHeightMeasureSpec);
  93. }
  94. }
  95. }

总结

测量始于DecorView,通过不断的遍历子View的measure方法,根据ViewGroup的MeasureSpec及子View的LayoutParams来决定子View的MeasureSpec,进一步获取子View的测量宽高,然后逐层返回,不断保存ViewGroup的测量宽高;

单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View;

原文链接:https://mp.weixin.qq.com/s/egu-869d0KU_89p6OWJoDA