MeasureSpec的理解和源码解析
在Android开发中,MeasureSpec是一个用于测量视图大小的类。它有两个主要属性:mode和size。mode表示尺寸测量方式,size表示测量结果的大小。MeasureSpec的创建规则如下:
1. 当mode为MODE_EXACTLY时,size必须与期望的大小相等。如果视图的实际大小小于期望的大小,则会抛出异常。
2. 当mode为MODE_AT_MOST时,size必须小于或等于期望的大小。如果视图的实际大小大于期望的大小,则实际大小会被使用。
3. 当mode为MODE_UNSPECIFIED时,系统会根据视图的实际需求来选择合适的mode。
4. 当mode为INVALID时,会抛出异常。
MeasureSpec实例详解:
下面通过一个简单的示例来演示MeasureSpec的使用。假设我们有一个TextView,需要为其设置宽度和高度。首先,我们需要获取到TextView的MeasureSpec对象,然后根据其mode属性来设置TextView的宽度和高度。
```java
// 获取TextView的MeasureSpec对象
int widthMeasureSpec = getView().getWidth();
int heightMeasureSpec = getView().getHeight();
// 获取期望的宽度和高度值
int desiredWidth = 200;
int desiredHeight = 100;
// 根据MeasureSpec的mode属性来设置TextView的宽度和高度
if (widthMeasureSpec == MeasureSpec.EXACTLY) {
getView().setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
} else if (widthMeasureSpec == MeasureSpec.AT_MOST) {
int actualWidth = getView().getMeasuredWidth();
if (actualWidth <= desiredWidth) {
getView().setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
} else {
getView().setLayoutParams(new ViewGroup.LayoutParams(actualWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
}
} else if (widthMeasureSpec == MeasureSpec.UNSPECIFIED) {
int actualWidth = getView().getMeasuredWidth();
int preferredWidth = Math.min(desiredWidth, actualWidth);
getView().setLayoutParams(new ViewGroup.LayoutParams(preferredWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
} else if (widthMeasureSpec == MeasureSpec.INVALID) {
// 如果MeasureSpec无效,则抛出异常
throw new IllegalArgumentException("Invalid MeasureSpec");
}
```
通过以上示例,我们可以看到如何根据MeasureSpec的mode属性来设置视图的宽度和高度。需要注意的是,当使用MODE_UNSPECIFIED时,我们需要确保视图的实际大小不会超过期望的大小。否则,可能会导致布局问题或者性能问题。
```java
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
public class UnderstandMeasureSpec {
public static void main(String[] args) {
// Your code here
}
/**
* @author http://blog.csdn.net/lfdfhl
* @date 2013-05-16
* @description 理解MeasureSpec
*/
private static void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 1. 获取子视图的LayoutParams
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 2. 获取子视图宽的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
// 3. 获取子视图高的MeasureSpec
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
// 4. 测量子视图
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
/**
* getChildMeasureSpec方法确定子View的MeasureSpec。
* @param spec 父容器的测量模式和可用大小。例如: MeasureSpec.EXACTLY、MeasureSpec.AT_MOST或MeasureSpec.UNSPECIFIED。
* @param padding 父容器在水平方向或者垂直方向已被占用的空间大小。
* @param childDimension 通过子View的LayoutParams获取的子View的宽或高。
* @return 返回计算得到的子View的测量规范。根据specMode和size值的不同,返回不同的规范。例如: MeasureSpec.EXACTLY、MeasureSpec.AT_MOST或MeasureSpec.UNSPECIFIED。并设置到resultSize中。
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0){
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT || childDimension == LayoutParams.WRAP_CONTENT){
resultSize = specSize;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT){
resultSize = Math.max(0, specSize - padding);
resultMode = (childDimension > resultSize)? MeasureSpec.AT_MOST: MeasureSpec.EXACTLY;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >=0){
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT || childDimension == LayoutParams.WRAP_CONTENT){
resultSize = specSize;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED: { if (childDimension >=0){ resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT || childDimension == LayoutParams.WRAP_CONTENT){ resultSize = specSize; } else if (childDimension == LayoutParams.WRAP_CONTENT){ resultSize = Math.max(0, specSize - padding); resultMode = (childDimension > resultSize)? MeasureSpec.AT_MOST: MeasureSpec.EXACTLY; }} default: break; } } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } } } } } } } }
如果您有任何疑问,请在评论区留言或加入本站社区进行交流讨论。感谢您的阅读,我们非常期待能够为您提供帮助。衷心感谢大家对本站的支持!
同时,您可能会对我们以下文章感兴趣: