自定义View——Measure

  1. 主要思路
    遍历递归 (递的是MeasureSpec 归的是measureWidth/Height

  2. 主体函数
    View.measure()View.onMeasure()View.setMeasuredDimension()

  3. ViewGroup.LayoutParams

    • 作用:用来告诉parent view布局的样式
    • public static final int MATCH_PARENT = -1;等于父布局大小-padding
    • public static final int WRAP_CONTENT = -2;要求视图足够大以适应内容大小
  4. MeasureSpec

    • 作用:父布局对子布局的布局要求。因为子布局的测量是受限于父布局的大小宽高等,所以在测量子布局之前,必须知道父布局的布局要求,measureSpec的作用就是用来包装父布局传递到子布局的布局要求(size&mode)的一个容器。实际布局要求的描述是由32位的int值表示(前两位表示mode,后30位表示其测量大小)
    • UNSPECIFIED:父布局对子布局没有任何约束,子布局可以是任意大小
    • EXACTLY:父布局给定确定的值,子布局限定在此确定值内,比如父布局是match_parent20dp
    • AT_MOST:子布局可以任意增大,直到其指定的大小,比如父布局是wrap_content
    • 相关源码:
      android/view/View.java
      sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
      public static class MeasureSpec {
          //移位位数位30位 因为
          private static final int MODE_SHIFT = 30;
          //0x3 16进制转为2进制为 11 右移30位为 110000...00
          //位掩码,用来与size和mode进行 & 运算 获取对应值
         private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
          //0 右移30位 0000000...0000
          public static final int UNSPECIFIED = 0 << MODE_SHIFT;
          //1 右移30位 01000000...0000
          public static final int EXACTLY     = 1 << MODE_SHIFT;
          //2 右移30位 10000000...0000
          public static final int AT_MOST     = 2 << MODE_SHIFT;
          //生成MeasureSpec包装的32位int值
          public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int                                      size,@MeasureSpecMode int mode) {
              //返回 前2位 为mode位 后30位 为 size布局大小 的int数值
             if (sUseBrokenMakeMeasureSpec) {
                  //sdk<17时用相加的方法
                 return size + mode;
             } else {
                  //sdk>17时,用位运算符
                 return (size & ~MODE_MASK) | (mode & MODE_MASK);
             }
         }
          //获取mode
          public static int getMode(int measureSpec) {
             return (measureSpec & MODE_MASK);
         }
          //获取size
          public static int getSize(int measureSpec) {
             return (measureSpec & ~MODE_MASK);
          }
      }
      
  5. measure(int widthMeasureSpec,int heightMeasureSpec)

    • 作用:执行测量的函数,公共逻辑部分,final修饰,不能重写。
    • 如何开始:ViewRootImpl调用DecorViewmeasure,并将 用32位int描述的布局要求向下传递。
    • 相关源码:
      android/view/ViewRootImpl.java
      private void performTraversals() {
          ... ...
          //获取宽度布局要求
          int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
          //获取高度布局要求
         int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
          performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          ... ...
      }
      
      android/view/ViewRootImpl.java
      //根据windowSize和LayoutParams计算MeasureSpec包装后的int值
      private static int getRootMeasureSpec(int windowSize, int rootDimension) {
          int measureSpec;
          switch (rootDimension) {
          case ViewGroup.LayoutParams.MATCH_PARENT:
              //window不可被调整,rootView被限定在window大小内
              measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
              break;
          case ViewGroup.LayoutParams.WRAP_CONTENT:
              //window可被调整为rootView的大小
              measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
              break;
          default:
              //window需要一个精确的值,rootView也是这个精确的值
              measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
              break;
          }
          //返回计算MeasureSpec包装后的int值
          return measureSpec;
      }
      
      android/view/ViewRootImpl.java
      private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
          //执行DecorView.measure mView是在setView中赋值的,具体查看1KnowledgeBackground
          mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
      
      android/view/View.java
      public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
          ... ...
          onMeasure(widthMeasureSpec, heightMeasureSpec);
          ... ...
      }
      
  6. onMeasure

    • 作用:真正执行测量的函数,开发者仅能重写该函数,实现自定义view的测量。
    • 默认测量策略:
      android/view/View.java
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                  getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
      }
      
      protected int getSuggestedMinimumWidth() {
          //当view设置了minWidth或者background属性时,返回其属性的值,作为默认最小宽度
         return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
      }
      
      public static int getDefaultSize(int size, int measureSpec) {
          int result = size;
          //获取父布局传过来的布局模式
          int specMode = MeasureSpec.getMode(measureSpec);
          //获取父布局传过来的布局大小
          int specSize = MeasureSpec.getSize(measureSpec);
          switch (specMode) {
          case MeasureSpec.UNSPECIFIED:
              //当且仅当父布局模式为UNSPECIFIED时,其宽高才会为getSuggestedMinimumWidth中返回的值
              result = size;
              break;
          case MeasureSpec.AT_MOST:
          case MeasureSpec.EXACTLY:
              //父布局模式为AT_MOST或EXACTLY时,默认都会以父布局的布局大小返回,也就是默认都是填充父布局。
              result = specSize;
              break;
          }
          return result;
      }
      
  7. setMeasuredDimension(int measuredWidth, int measuredHeight)

    • 作用:完成测量的函数,并将onMeasure中测量的结果存储到mMeasuredWidthmMeasureHeight
    • 相关源码:
      android/view/View.java
      protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
             ... ...
          setMeasuredDimensionRaw(measuredWidth, measuredHeight);
      }
      
      private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
         mMeasuredWidth = measuredWidth;
         mMeasuredHeight = measuredHeight;
      }
      
  8. getMeasuredHeight/Width

    • 作用:返回测量流程结束后的结果。
    • 相关源码:
      android/view/View.java
      public static final int MEASURED_SIZE_MASK = 0x00ffffff;
      
      public final int getMeasuredWidth() {
          return mMeasuredWidth & MEASURED_SIZE_MASK;
      }
      
      public final int getMeasuredHeight() {
          return mMeasuredHeight & MEASURED_SIZE_MASK;
      }
      
  9. 递归简要流程:(虚线是递流程 实线是归流程)

  10. 其他要点

    • getMeasuredHeight/WidthgetHeight/Width区别:
      示意图
  11. 系列文章

    1. View的背景知识
    2. View的测量流程
    3. View的布局流程
    4. View的绘制背景知识
    5. View的绘制流程
    6. View的三大绘制流程总结
    7. View的事件分发机制

你可能感兴趣的:(自定义View——Measure)