match_parent、wrap_parent、具体值 和 MeasureSpec 类中 mode 的对应关系

来源:互联网 发布:dwg mac 编辑:程序博客网 时间:2024/05/21 10:33

如果不考虑父 view 的影响,测试结果如下:

 * wrap_parent -> MeasureSpec.AT_MOST
 * match_parent -> MeasureSpec.EXACTLY
 * 具体值 -> MeasureSpec.EXACTLY

这是一个符合我们直觉的结果。

一个 view 的 onMeasure 方法最终得到的测量规格值(测量约束值)中包含的测量模式和上面不一定对的上,这是因为 onMeasure 方法中得到的测量规格值(测量约束值)是 measure 方法传过来的,父 view 在调用 measure 方法的时候可以根据自己的情况不使用 ViewGroup 提供的 measureChildren 方法,而改变上面的映射关系,比如 RelativeLayout 中就结合其他 Layout 属性综合确定 MeasureSpec 中的模式。上面的映射关系可以直接查看 ViewGroup 类的 getChildMeasureSpec 方法,这是最简单的 measure 子 view 的方式.

ViewGroup 中 measureChild 方法如下:

    protected void measureChild(View child, int parentWidthMeasureSpec,            int parentHeightMeasureSpec) {        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 方法如下:

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0, specSize - padding);        int resultSize = 0;        int resultMode = 0;        switch (specMode) {        // Parent has imposed an exact size on us        case MeasureSpec.EXACTLY:            if (childDimension >= 0) {                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size. So be it.                resultSize = size;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent has imposed a maximum size on us        case MeasureSpec.AT_MOST:            if (childDimension >= 0) {                // Child wants a specific size... so be it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size, but our size is not fixed.                // Constrain child to not be bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent asked to see how big we want to be        case MeasureSpec.UNSPECIFIED:            if (childDimension >= 0) {                // Child wants a specific size... let him have it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size... find out how big it should                // be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how                // big it should be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;        }        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }
我们父 view 是 match_parent 情况下我们只需要关注 case MeasureSpec.EXACTLY 就行了。view 自带的 geDefaultSize 这个方法如果设置包裹内容,最后得到的 view 不会是包裹内容,而是父 view 指定的大小。正如 geDefaultSize 这个方法中 switch 代码中所展示的逻辑。所以 getDefaultSize 这个方法还是很粗糙的方法,连 wrap_content 这个属性都没有提供支持,当然对 match_parent 和 指定值 还是有支持的。这也就是说 View 中自带的 onMeasure 其实可以用性很低。也难怪 reference 文档中说 子 view 必须重新 onMeasure。ViewGroup 中的 measureChildren 方法没有对 margin 进行处理,所以如果子 view 的属性带 margin,那么他其实是不能给子 view 一个结合 margin 的测量约束值。


测试代码如下

TestView.java

/** * 测试 match_parent、wrap_parent、具体值 和 MeasureSpec 中  mode 的对应关系 */package com.example.testmeasurespec;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.View;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) {        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;    }}

TestLayout.java

public class TestLayout extends ViewGroup {        public TestLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    public TestLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public TestLayout(Context context) {        super(context);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {    }        @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        measureChildren(widthMeasureSpec, heightMeasureSpec);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }}

main_activity.xml

<com.example.testmeasurespec.TestLayout 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" >    <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" /></com.example.testmeasurespec.TestLayout>


1 0
原创粉丝点击