MeasureSpec中的测量模式和match_parent、wrap_content是什么对应关系?

下面是我的测试程序, 重写了 View 中的 getDefaultSize 方法,打印出每次调用 getDefaultSize 的时候传递进来的 measureSpec 的模式,在主 activity 的 xml 中放了 3 个 TestView,分别使用 wrap_content、match_parent、具体 dp 值,来指定 layout_width 和 layout_height 的值,期望通过 log 看到 3 个 view 在调用自己的 onMeasure 方法的时候传递进来的测量约束的模式。但是打印结果让我很糊涂,因为打印的结果和设置的值看起来并不是一一对应的关系?所以我想问,测量模式和 layout_width 的值的类型是到底什么对应关系?

MainActivity 布局文件如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testmeasurespec.MainActivity" >

    <com.example.testmeasurespec.TestView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_launcher"
        android:tag="test1" />

    <com.example.testmeasurespec.TestView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/ic_launcher"
        android:tag="test2" />

    <com.example.testmeasurespec.TestView
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:background="@drawable/ic_launcher"
        android:tag="test3" />

</RelativeLayout>

测试 TestView 类如下:

public class TestView extends View {

    private static final String TAG = "TestView";

    public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TestView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        Log.i(TAG, "onMeasure:" + getTag());
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                Log.i(TAG, "specMode:MeasureSpec.UNSPECIFIED");
                result = size;
                break;
            case MeasureSpec.AT_MOST:
                Log.i(TAG, "specMode:MeasureSpec.AT_MOST");
            case MeasureSpec.EXACTLY:
                Log.i(TAG, "specMode:MeasureSpec.EXACTLY");
                result = specSize;
                break;
        }
        return result;
    }
}

打印结果如下:
11-21 16:13:48.812: I/TestView(21213): onMeasure:test3
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): onMeasure:test2
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): onMeasure:test1
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): onMeasure:test3
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): onMeasure:test2
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): onMeasure:test1
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.812: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.892: I/TestView(21213): onMeasure:test3
11-21 16:13:48.892: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.892: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.892: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.892: I/TestView(21213): onMeasure:test2
11-21 16:13:48.892: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.892: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): onMeasure:test1
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): onMeasure:test3
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): onMeasure:test2
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): onMeasure:test1
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.AT_MOST
11-21 16:13:48.902: I/TestView(21213): specMode:MeasureSpec.EXACTLY

阅读 13.7k
1 个回答
新手上路,请多包涵

最简单的映射关系是:
* wrap_parent -> MeasureSpec.AT_MOST
* match_parent -> MeasureSpec.EXACTLY
* 具体值 -> MeasureSpec.EXACTLY

但是上面代码由于放在 RelativeLayout 中,RelativeLayout 是一个比较复杂的 ViewGroup,其中子 view 的大小不仅跟 layout_width、layout_height 属性相关,还更很多属性有关系,所以会改变上述映射情况,使结果变得特别复杂。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏