
本文共 4534 字,大约阅读时间需要 15 分钟。
MeasureSpec类及其在Android测量中的应用
Android中的MeasureSpec类是View系统中用于测量子视图尺寸的关键机制,主要用于确定子视图的宽度和高度。MeasureSpec提供三种测量模式:UNSPECIFIED、EXACTLY和AT_MOST。了解这些模式的工作原理,对于优化Android布局 layouts 是非常重要的。
MeasureSpec类概述
MeasureSpec类用于描述一个控件的尺寸限制,主要分为三种模式:
UNSPECIFIED(未定义):表示尺寸没有被明确限制,可以为0或任意值。
EXACTLY(准确):表示尺寸被明确指定为一个特定的值。
AT_MOST(最多):表示尺寸不超过某个最大值,子控件可以根据需要选择适当的尺寸。
获取MeasureSpec的模式和尺寸
通过静态方法getMode和getSize,可以获取MeasureSpec的当前模式和尺寸值:
public static int getMode(int measureSpec);public static int getSize(int measureSpec);
View的测量方法
每个View都需要通过measure方法(measure(int, int))来计算它的宽度和高度。这通常包括以下步骤:
获取测量器(MeasureSpec):测量器由父控件提供,包含宽度和高度的限制。
执行或缓存测量:如果有forceLayout布局强制刷新,会立即进行测量;否则会尝试先从缓存中获取。
计算尺寸:通过onMeasure方法或setMeasuredDimensionRaw方法设置测量结果。
具体实现
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { // 如果有布局强制刷新,立即测量,否则尝试从缓存中获取结果 int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // urch herself measured,设置测量结果 onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { // 从缓存中获取测量结果 long value = mMeasureCache.valueAt(cacheIndex); // 可以通过位运算忽略高32位,直接设置尺寸 setMeasuredDimensionRaw((int)(value >> 32), (int)value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }}
ViewGroup的测量Child方法
ViewGroup不仅需要测量自身尺寸,还需要处理子控件的测量。系统提供了以下方法来实现这一功能:
measureChildren(int, int):遍历所有直接子控件,只有View.GONE以外的控件会被测量。
measureChild(View, int, int):为单个子控件创建MeasureSpec,并调用其measure方法。
具体实现
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // 通过getChildMeasureSpec方法为子控件创建MeasureSpec final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec( parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width ); final int childHeightMeasureSpec = getChildMeasureSpec( parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height ); // 调用子控件的测量方法 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
getChildMeasureSpec方法
getChildMeasureSpec方法根据父控件的MeasureSpec和子控件的布局属性(如MATCH_PARENT、WRAP_CONTENT)来确定子控件的尺寸。
具体实现
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); // 根据specMode判断子控件尺寸的限制 switch (specMode) { case EXACTLY: { if (childDimension >= 0) { // 指定为具体值 return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY); } else if (childDimension == LayoutParams.MATCH_PARENT) { // 子控件尺寸与父控件一致 return MeasureSpec.makeMeasureSpec(specSize - padding, EXACTLY); } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 子控件宽度由内容决定,不超过父控件尺寸 return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST); } break; case AT_MOST: { if (childDimension >= 0) { // 允许子控件指定尺寸 return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY); } else if (childDimension == LayoutParams.MATCH_PARENT) { // 子控件尺寸与父控件一致,但父控件尺寸不定 return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST); } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 子控件宽度由内容决定,不超过父控件尺寸 return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST); } break; case UNSPECIFIED: { if (childDimension >= 0) { // 允许子控件指定尺寸 return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY); } else if (childDimension == LayoutParams.MATCH_PARENT) { // 子控件尺寸与父控件一致 return MeasureSpec.makeMeasureSpec(specSize - padding, UNSPECIFIED); } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 子控件宽度由内容决定 return MeasureSpec.makeMeasureSpec(specSize - padding, UNSPECIFIED); } break; } } // 返回默认MeasureSpec return childDimension == 0 ? MeasureSpec.UNSPECIFIED : MeasureSpec.makeMeasureSpec(childDimension, EPSILON);}
相关文章
通过理解MeasureSpec类和 τις ViewGroup测量方法,可以更好地优化Android布局,提升应用性能和用户体验。
发表评论
最新留言
关于作者
