Android视图测量流程(测量)全面解析

  

<强>前言

  

上一篇文章,笔者主要讲述了DecorView以及ViewRootImpl相关的作用,这里回顾一下上一章所说的内容:DecorView是视图的顶级视图,我们添加的布局文件是它的一个子布的局,而ViewRootImpl则负责渲染视图,它调用了一个performTraveals方法使得ViewTree开始三大工作流程,然后使得观点展现在我们面前。本篇文章主要内容是:详细讲述视图的测量(测量)流程,主要以源码的形式呈现,源码均取自Android API 21。

  

<强>从ViewRootImpl # PerformTraveals说起

  

我们直接从这个方法说起,因为它是整个工作流程的核心,我们看看它的源码:

        私人空间performTraversals () {   …      如果(! mStopped) {   int childWidthMeasureSpec=getRootMeasureSpec (mWidth lp.width);//1   int childHeightMeasureSpec=getRootMeasureSpec (mHeight lp.height);   performMeasure (childWidthMeasureSpec childHeightMeasureSpec);   }   }      如果(didLayout) {   performLayout (lp desiredWindowWidth desiredWindowHeight);   …   }         如果(!cancelDraw,,! newSurface) {   如果(!skipDraw | | mReportNextDraw) {   如果(mPendingTransitions !=零,,mPendingTransitions.size()比;0){   for (int i=0;我& lt;mPendingTransitions.size ();+ + i) {   mPendingTransitions.get (i) .startChangingAnimations ();   }   mPendingTransitions.clear ();   }      performDraw ();   }   }   …   }      之前      

方法非常长,这里做了精简,我们看到它里面主要执行了三个方法,分别是performMeasure, performLayout, performDraw这三个方法,在这三个方法内部又会分别调用测量、布局,画这三个方法来进行不同的流程。我们先来看看performMeasure (childWidthMeasureSpec childHeightMeasureSpec)这个方法,它传入两个参数,分别是childWidthMeasureSpec和childHeightMeasure,那么这两个参数代表什么意思呢?要想了解这两个参数的意思,我们就要先了解MeasureSpec。

  

<强>理解MeasureSpec

  

MeasureSpec是视图类的一个内部类,我们先看看官方文档对MeasureSpec类的描述:MeasureSpec封装了布局要求从父母传给孩子。每个MeasureSpec代表一个宽度或高度要求。MeasureSpec由大小和一个模式。它的意思就是说,该类封装了一个视图的规格尺寸,包括视图的宽和高的信息,但是要注意,MeasureSpec并不是指视图的测量宽高,这是不同的,是根据MeasueSpec而测出测量宽高。
  MeasureSpec的作用在于:在测量流程中,系统会将视图的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后在onMeasure方法中根据这个MeasureSpec来确定视图的测量宽高。
  我们来看看这个类的源码:

        公共静态类MeasureSpec {   私有静态最终int MODE_SHIFT=30;   私有静态最终int MODE_MASK=0 x3 & lt; & lt;MODE_SHIFT;/* *   *未指明的模式:   *父视图不对子视图有任何限制,子视图需要多大就多大   */公共静态最终int不明=0 & lt; & lt;MODE_SHIFT;/* *   * EXACTYLY模式:   *父视图已经测量出子另外所需要的精确大小,这时候视图的最终大小   *就是SpecSize所指定的值。对应于match_parent和精确数值这两种模式   */公共静态最终int=1 & lt; & lt;MODE_SHIFT;/* *   * AT_MOST模式:   *子视图的最终大小是指父视图定的SpecSize值,并且子视图的大小不能大于这个值,   *即对应wrap_content这种模式   */公共静态最终int AT_MOST=2 & lt; & lt;MODE_SHIFT;//将规模和模式打包成一个32位的整数型数的值//高2位表示SpecMode,测量模式,低30位表示SpecSize,某种测量模式下的规格大小   公共静态int makeMeasureSpec (int, int模式){   如果(sUseBrokenMakeMeasureSpec) {   返回大小+模式;   其他}{   返回(大小,~ MODE_MASK) |(模式,MODE_MASK);   }   }//将32位的MeasureSpec解包,返回SpecMode,测量模式   公共静态int getMode (int measureSpec) {   返回(measureSpec,MODE_MASK);   }//将32位的MeasureSpec解包,返回SpecSize,某种测量模式下的规格大小   公共静态int getSize (int measureSpec) {   返回(measureSpec,~ MODE_MASK);   }//?   }      之前      

可以看的出,该类的思路是相当清晰的,对于每一个视图中,包括DecorView,都持有一个MeasureSpec,而该MeasureSpec则保存了该视图的尺寸规格。在视图的测量流程中,通过makeMeasureSpec来保存宽高信息,在其他流程通过getMode或getSize得到模式和宽高。那么问题来了,上面提到MeasureSpec是LayoutParams和父容器的模式所共同影响的,那么,对于DecorView来说,它已经是顶层视图了,没有父容器,那么它的MeasureSpec怎么来的呢?

Android视图测量流程(测量)全面解析