MeasureSpec再理解

MeasureSpec封装了父view传递给子view的布局要求,代表了一组高度和宽度的要求。

一个MeasureSpec由大小(size)(mode)组成。

模式有三种:

  • UNSPECIFIED:这个一般是系统自己用到的。父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小。
  • EXACTLY:当传入具体的值或者是match_parent时
  • AT_MOST:当设置成wrap_content时,这时的大小是不确定的。view会根据上线来设置自己的大小。

常用函数:

  1. static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
  2. static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
  3. static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)

onMeasure传入的两个参数是由上一层控件传入的大小,有多种情况,重写该方法时需要对计算控件的实际大小,然后调用setMeasuredDimension(int, int)设置实际大小。
我们需要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。

一个重写的实例:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by panhongchao on 16/7/8.
 */
public class TestView extends View {
    public TestView(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 10;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 20;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
}

measure的测量流程

viewGroup

重要是在onMeasure(),遍历每个子view的测量。

view

入口为ViewGroup#measureChildWithMargin()方法。

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 1
}

由源码可知,里面调用了getChildMeasureSpec方法,把父容器的MeasureSpec以及自身的layoutParams属性传递进去来获取子View的MeasureSpec,这也印证了子View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定这个结论。

具体的逻辑关系:

接着调用View#measure,内部再调用View#onMeasure。而这个measure是由下层的view重写的。

results matching ""

    No results matching ""