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); } } } } } } } }

如果您有任何疑问,请在评论区留言或加入本站社区进行交流讨论。感谢您的阅读,我们非常期待能够为您提供帮助。衷心感谢大家对本站的支持!

同时,您可能会对我们以下文章感兴趣: