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流程。
关注我