美文网首页
组件测量那些事

组件测量那些事

作者: cxlin007 | 来源:发表于2016-09-12 11:02 被阅读13次

    简介

    组件在页面上的展示要经过测量、布局、绘制三个过程,而测量就是把我们定义的组件宽和高转换为具体的大小的这个过程。
    我们常见的组件的宽高定义如下:

    <TextView
        android:id="@+id/name1"
        android:layout_width="100dp"
        android:layout_height="100dp" />
    <TextView
        android:id="@+id/name1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/name1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

    宽高定义为100dp,那么最后组件的宽高就是100dp,这很好理解,那wrap_content和match_parent呢,我们都知道,wrap_content根据组件自身的大小变化而变化,match_parent指的要占满父视图。那么系统具体是如何解析的呢?

    系统把组件的长宽定义转换相应的测量规则

    在页面测量的时候都会调用measureChild或measureChildWithMargins方法对组件进行测量,已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);}
    

    关于MeasureSpec的定义就不再这里阐述了,大家可以查看其他文章。
    这段代码很短,主要就是描述了通过getChildMeasureSpec把父视图的测量规则和组件的布局参数转换为子视图的测量规则,进行测量。再来看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:
           ... 
       }
       return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
    

    1、如果childDimension >0,指的就是组件定义了具体的大小(MATCH_PARENT为-1,WRAP_CONTENT为-2),例如:

    <TextView
        android:id="@+id/name1"
        android:layout_width="100dp"
        android:layout_height="100dp" />
    

    组件的大小就是childDimension ,测量模式为MeasureSpec.EXACTLY。
    2、如果childDimension == LayoutParams.MATCH_PARENT,组件大小就是父视图提供的大小size,而测量模式也是MeasureSpec.EXACTLY。
    3、如果childDimension == LayoutParams.WRAP_CONTENT,组件的测量模式为MeasureSpec.AT_MOST,大小为父视图提供的大小size,这里的大小实际上指的是组件的最大大小,组件的具体大小的测量还需要组件自己实现。
    4、如果父视图的测量模式为MeasureSpec.AT_MOST,及时组件childDimension == LayoutParams.MATCH_PARENT,其测量模式也为MeasureSpec.AT_MOST。

    组件如何根据测量模式测量出其大小的

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec)方法最终就会调用组件的onMeasure方法,在这里组件会根据测量规则计算出具体的宽高大小。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
    

    getDefaultSize方法就是计算出具体的宽高,setMeasuredDimension方法则是设置宽高。
    基类View的具体计算方法是getDefaultSize:

    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:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
    

    无论是MeasureSpec.AT_MOST还是MeasureSpec.EXACTLY模式,最终得到的具体大小都为父视图提供大小,这与我们对wrap_content的理解是有出入的。其实具体的组件都需要重写onMeasure方法的。
    例如TextView:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            width = widthSize;
        } else {
          ...这里计算textView的大小width....       
            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(widthSize, width);
            }
        }
    ...
    setMeasuredDimension(width, height);
    }
    

    1、如果模式为MeasureSpec.EXACTLY,大小就是父视图提供的大小
    2、如果模式是MeasureSpec.AT_MOST,会取textView的真实大小与与父视图提供的大小中小的那个,这样与我们理解的wrap_content就吻合了。

    相关文章

      网友评论

          本文标题:组件测量那些事

          本文链接:https://www.haomeiwen.com/subject/jerpettx.html