Android

Android控件测量-MesaureSpec详解

2021-02-27  本文已影响0人  Timmy_zzh

1.View的测量

MeasureSpec
public class View implements ... {
  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        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:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
  
    protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight,     mBackground.getMinimumHeight());
    }
}

2.ViewGroup的测量

ViewGroup的onMeasure
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
  
        protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

        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);
    }

    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);
    }
}

其中LayoutParams源码如下:

    public static class LayoutParams {
        @Deprecated
        public static final int FILL_PARENT = -1;
        public static final int MATCH_PARENT = -1;
        public static final int WRAP_CONTENT = -2;
        
        public int width;
        public int height;
 
        public LayoutParams(int width, int height) {
            this.width = width;
            this.height = height;
        }
      
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            width = a.getLayoutDimension(widthAttr, "layout_width");
            height = a.getLayoutDimension(heightAttr, "layout_height");
        }
   }
getChildMeasureSpec
public abstract class ViewGroup extends View implements ViewParent, ViewManager {

    //参数一:spec 为父控件的MeasureSpec
    //参数二:padding 父空间已消耗的大小
    //参数三:childDimension 子View的LayoutParams中的宽高值
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
      
        //父控件的测量模式和大小
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        //size为留给子view剩余的最大空间
        int size = Math.max(0, specSize - padding);

        //最终需要计算确定的子view的大小和模式,并组合成MeasureSpec返回
        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // 父控件测量模式是 EXACTLY,表示父控件大小为固定值
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {          
                    //表示子view的大小设置为固定值--》则传递给子类的大小为固定值和EXACTLY模式
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 表示子view大小设置为MATCH_PARENT,子view使用父控件剩余的可用空间
                //--》传递给子view的大小为固定值就是父控件的剩余可用空间和EXACTLY模式
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 表示子view的大小设置为WRAP_CONTENT包裹类型,
                // --》传递给子view的大小为父控件的剩余可用空间和AT_MOST模式
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // 父控件测量模式是 AT_MOST,表示父控件大小为包裹大小
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                //表示子view的大小设置为固定值--》则传递给子类的大小为子view的固定值和EXACTLY模式
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 表示子view大小设置为MATCH_PARENT,子view想使用父控件剩余的可用空间,
                // 但是父控件大小不固定,所以子控件也是固定不了,模式也转换成AT_MOST包裹类型
                //--》传递给子view的大小为固定值就是父控件的剩余可用空间和 AT_MOST 模式
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 父控件和子控件都使用WRAP_CONTENT 包裹模式
                // 传递给子view的大小为父控件的剩余可用空间和 AT_MOST 模式
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // 当父view的模式为UNSPECIFIED时,父容器不对view有任何限制,要多大给多大
        // 多见于ListView、GridView  
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // 子view大小为子自身所赋的值
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 因为父view为UNSPECIFIED,所以MATCH_PARENT的情况下,子类大小为0 
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 因为父view为UNSPECIFIED,所以 WRAP_CONTENT 的情况下,子类大小为0 
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
      
        //组装
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
}
子view布局参数(LayoutParams)\ 父控件测量模式 EXACTLY AT_MOST UNSPECIFIED
具体数值(dp/px) EXACTLY + childSize EXACTLY + childSize EXACTLY + childSize
match_parent EXACTLY + parentSize(父容器剩余空间) AT_MOST + parentSize(大小不能超过父容器的剩余空间) UNSPECIFIED + 0
wrap_content AT_MOST + parentSize(大小不能超过父容器的剩余空间) AT_MOST + parentSize(大小不能超过父容器的剩余空间) UNSPECIFIED + 0
规律总结
上一篇下一篇

猜你喜欢

热点阅读