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