MeasureSpec作为View中的一个很关键的内部静态类,在View的绘制过程中起到了很重要的作用。MeasureSpec参与了View的measure过程,很大程度上决定了View的尺寸规格。
在测量过程中,Android系统会将View的布局参数(LayoutParams)根据父容器所施加的规则,转化为MeasureSpec。然后,再根据这个measureSpec来测量View的宽度/高度。
首先看一下MeasureSpec的具体源码。
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);
}
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();
}
}
SpecMode有三类:UNSPECIFIED, EXACTLY, AT_MOST
UNSPECIFIED:父容器不对View有任何限制,即要多大的空间就给多大空间。这种情况一般用于Android系统内部,表示一种测量的状态。
EXACTLY:父容器已经检测出View所需要的精确大小,这时View的最终大小就是SpecSize所指定的值。这种模式对应的就是LayoutParams中的match_parent和具体的数值这两种模式。
AT_MOST:对应于LayoutParams中的wrap_content,即父容器指定一个可用的SpecSize,View的大小不能大于这个值。
上文中已经说过,Android系统会将View的布局参数(LayoutParams)根据父容器所施加的规则,转化为MeasureSpec。然后,再根据这个measureSpec来测量View的宽度/高度。需要注意的是,MeasureSpec不是唯一由LayoutParams决定的,layoutParams需要和父容器一起决定View的MeasureSpec,从而进一步决定View的宽/高。需要特别说明的是,DecorView(顶级View)和普通View,转换过程略有不同。
DecorView:MeasureSpec由窗口的尺寸和其自身的LayoutParams共同决定;
普通View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。
不管是哪种情况,一旦MeasureSpec确立之后,onMeasure方法就可以确定View的测量宽度和高度了。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/36464.html