MeasureSpec是View的内部类,MeasureSpec 封装了从父级传递到子级的测量要求。每个 MeasureSpec 代表对宽度或高度的要求。 MeasureSpec 由大小和模式两部分组成。
更详细的说明请看 Google官方文档
下面是MeasureSpec的源码:(基于Android 12)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
   | public static class MeasureSpec {     private static final int MODE_SHIFT = 30;     private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
      public static final int UNSPECIFIED = 0 << MODE_SHIFT;
      public static final int EXACTLY     = 1 << MODE_SHIFT;
      public static final int AT_MOST     = 2 << MODE_SHIFT;
      public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,                                       @MeasureSpecMode int mode) {         if (sUseBrokenMakeMeasureSpec) {             return size + mode;         } else {             return (size & ~MODE_MASK) | (mode & MODE_MASK);         }     }
      public static int makeSafeMeasureSpec(int size, int mode) {         if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {             return 0;         }         return makeMeasureSpec(size, mode);     }
      public static int getMode(int measureSpec) {         return (measureSpec & MODE_MASK);     }
      public static int getSize(int measureSpec) {         return (measureSpec & ~MODE_MASK);     }
      static int adjust(int measureSpec, int delta) {         final int mode = getMode(measureSpec);         int size = getSize(measureSpec);         if (mode == UNSPECIFIED) {             return makeMeasureSpec(size, UNSPECIFIED);         }         size += delta;         if (size < 0) {             Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +                     ") spec: " + toString(measureSpec) + " delta: " + delta);             size = 0;         }         return makeMeasureSpec(size, mode);     }
      public static String toString(int measureSpec) {         int mode = getMode(measureSpec);         int size = getSize(measureSpec);
          StringBuilder sb = new StringBuilder("MeasureSpec: ");
          if (mode == UNSPECIFIED)             sb.append("UNSPECIFIED ");         else if (mode == EXACTLY)             sb.append("EXACTLY ");         else if (mode == AT_MOST)             sb.append("AT_MOST ");         else             sb.append(mode).append(" ");
          sb.append(size);         return sb.toString();     } }
  | 
 
从源码可以看出,它代表了32位int值,其中高2位代表mode,低30位代表size。mode指测量模式,size指测量值。mode有3种模式:
UNSPECIFIED:未指定模式,父View没有对子View施加任何限制。它可以是任何它想要的大小。
 
EXACTLY:精确模式,父View已经确定了子View的确切尺寸。无论子View想要多大,都将获得这些界限。
 
AT_MOST:最大模式,子View可以根据需要达到指定的最大的大小。
 
makeMeasureSpec用来保存宽和高的信息,getMode用来获取测量模式,getSize用来获取测量大小。
MeasureSpec受自身LayoutParams和父View的MeasureSpec共同影响。作为顶层View的DecorView来说,其并没有父View,那它的MeasureSpec是如何得来的呢?我们回到ViewRootImpl的performTraversals方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   |  if (!mStopped || wasReportNextDraw) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally(         (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()         || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||         updatedConfiguration) {     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
      if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="             + mWidth + " measuredWidth=" + host.getMeasuredWidth()             + " mHeight=" + mHeight             + " measuredHeight=" + host.getMeasuredHeight()             + " dispatchApplyInsets=" + dispatchApplyInsets);
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
 
  | 
 
再看注释1处的getRootMeasureSpec方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | private static int getRootMeasureSpec(int windowSize, int rootDimension) {     int measureSpec;     switch (rootDimension) {
      case ViewGroup.LayoutParams.MATCH_PARENT:         measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);         break;     case ViewGroup.LayoutParams.WRAP_CONTENT:         measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);         break;     default:         measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);         break;     }     return measureSpec; }
  | 
 
第一个参数windowSize指窗口的尺寸,所以对于DecorView来说,它的MeasureSpec由自身的LayoutParams和窗口的尺寸决定,这一点和普通的View是不同的。
再回到上面看注释2处的performMeasure方法:
1 2 3 4 5 6 7 8 9 10 11
   | private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {     if (mView == null) {         return;     }     Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");     try {         mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);     } finally {         Trace.traceEnd(Trace.TRACE_TAG_VIEW);     } }
  | 
 
注释1处调用了mView.measure方法,进入了view的measure流程。
关注我