MeasureSpec详解

MeasureSpec详解1 MeasureSpec 简介 MeasureSpec 封装了从父级传递给子级的布局要求 每个 MeasureSpec 代表宽度或高度的要求 MeasureSpecs 实现为 32 位的 int 由 size 和 mode 组成 高 2 位代表 mode 低 30 位代表 size 它通过将 mode 和 size 打包成一个 int 值来减少对象内存分配 并提供打包和解包的方法 mode 分类 UNSPECIFIED 父容器不

1.MeasureSpec简介

  • MeasureSpec封装了从父级传递给子级的布局要求。 每个MeasureSpec代表宽度或高度的要求。
  • MeasureSpecs实现为32位的int,由size和mode组成,高2位代表mode,低30位代表size,它通过将mode和size打包成一个int值来减少对象内存分配,并提供打包和解包的方法。
  • mode分类:
    • UNSPECIFIED:父容器不对View有任何限制,给它想要的任何尺寸。一般用于系统内部,表示一种测量状态。
    • EXACTLY:父容器已经检测出view的精确大小,这时候view的大小就是size所指定的值。它对应于LayoutParams中的match_parent和具体数值两种模式。
    • AT_MOST:父容器指定了一个可用大小,即size,子view的大小不能大于这个值,具体值要看vew的实现。它对应于LayoutParams中的wrap_content。
 public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; / @hide */ @IntDef({UNSPECIFIED, EXACTLY, AT_MOST}) @Retention(RetentionPolicy.SOURCE) public @interface MeasureSpecMode {} 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); } @MeasureSpecMode public static int getMode(int measureSpec) { //noinspection ResourceType 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) { // No need to adjust size for UNSPECIFIED mode. 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); } } 

2.MeasureSpec与LayoutParams的关系

普通View的Measure参数是由父容器传递而来,这里我们可以看下ViewGroup的measureChildWithMargins方法:

 / * @param child 要测量的子view * @param parentWidthMeasureSpec 该容器对子view的宽度要求 * @param widthUsed 父级水平使用的额外空间(可能是父级的其他子级),可以理解为已经被占用的空间 * @param parentHeightMeasureSpec 该容器对子view的高度要求 * @param heightUsed 父级垂直使用的额外空间(可能是父级的其他子级) */ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } 

可以看到子View的测量参数是由getChildMeasureSpec方法得到的,父容器对子View的测量要求、父容器的Padding、子View的margin、父容器的已占空间最终算出了子View的MeasureSpec

 / * @param spec 该容器的测量要求 * @param padding 可以简单理解为子view和该容器之间的间隔,由多个值决定 * @param childDimension 子view的期望值,即子view的LayoutParams的宽高 */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } 

上述代码可以简单翻译成下面这个图表:

child\parent EXACTLY AT_MOST UNSPECIFIED
具体尺寸 EXACTLY、childSize EXACTLY、childSize EXACTLY、childSize
match_parent EXACTLY、parentSize AT_MOST、parentSize UNSPECIFIED、SDK<23?0:parentSize
wrap_content AT_MOST、parentSize AT_MOST、parentSize UNSPECIFIED、SDK<23?0:parentSize
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/222505.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午3:39
下一篇 2026年3月17日 下午3:39


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号