自定义View - 15.measure()源码分析




    

    

    



上面布局是如果测量的.

从performTraversals开始
第一个调用的方法:performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
--> mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-->onMeasure(widthMeasureSpec, heightMeasureSpec); //测量开始
-->LinearLayout.onMeasure(widthMeasureSpec, heightMeasureSpec);
-->measureVertical(widthMeasureSpec, heightMeasureSpec);
-->measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
-->measureChildWithMargins(child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight);
childWidthMeasureSpec,childHeightMeasureSpec测量模式,把这两个测量模式传给自View,这里就是TextView
childWidthMeasureSpec是怎么来的.

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;
        
}
public class TextView extends View {
    public TextView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //widthMeasureSpec == childWidthMeasureSpec
        //heightMeasureSpec == childHeightMeasureSpec

        //wrap_content 对应 AT_MOST
        //match_parent ,100dp 对应 EXACTLY

       //模式和大小是由父布局和自己决定的
      //比如 父布局是包裹内容,就算子布局是match_parent,这时候计算测量模式还是AT_MOST.
具体规则看.ViewGroup的getChildMeasureSpec方法.
    }
}

--> child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

这个时候我们都会调用setMeasuredDimension().这个时候我的布局才真正指定宽度和高度.这时候mMeasuredWidth和mMeasuredHeight才开始有值.

接着执行ViewGroup的onMeasure() 方法,这时候需要指定自己的宽高.
childHeight = child.getMeasuredHeight() + share;LinearLayout的高度算法如果是垂直方向是不断叠加子View的高度.

总结:
performMeasure():用于指定和测量layout中所有控件的宽高.
对于ViewGroup,先去测量里面子孩子,根据子孩子的宽高再来计算和指定自己的宽高.
对于View,它的宽高是由自己和父布局决定的.

你可能感兴趣的:(自定义View - 15.measure()源码分析)