背景

在阅读安卓各种view的onMeasure()方法时,会遇到一些被共同调用的方法,这里我做一个整理,以备来日查看


measureChildWithMargins

代码如下

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

主要调用了getChildMeasureSpec()方法


getChildMeasureSpec

代码如下

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        // 以宽度为例,传入的参数分别是:父view的widthMeasureSpec、已用宽度(子view左右内外间距+已用的间距)、子view要求的宽度        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0, specSize - padding); // 父宽度 - 子view的内外间距        // 也就是子view的最大宽度        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. sdk < 23时,sUseZeroUnspecifiedMeasureSpec为true                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how                // big it should be.                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;        }        //noinspection ResourceType        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }


combineMeasuredState

    public static int combineMeasuredStates(int curState, int newState) {        return curState | newState;    }

没啥好说的,合并两个state


getMeasuredState

public final int getMeasuredState() {        return (mMeasuredWidth&MEASURED_STATE_MASK)                | ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)                        & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));}

通过位运算合并高度和宽度的state,结果就是第1个字节是宽度的state,第3个字节是高度的state


resolveSizeAndState

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { // size是自己需要的尺寸,measureSpec是父view指定的尺寸        final int specMode = MeasureSpec.getMode(measureSpec);        final int specSize = MeasureSpec.getSize(measureSpec); // 父view指定的尺寸        final int result;        switch (specMode) {            case MeasureSpec.AT_MOST:                if (specSize < size) {                    result = specSize | MEASURED_STATE_TOO_SMALL; // 设定标志位,表示父view得分配的再大一些                } else {                    result = size;                }                break;            case MeasureSpec.EXACTLY:                result = specSize;                break;            case MeasureSpec.UNSPECIFIED:            default:                result = size;        }        return result | (childMeasuredState & MEASURED_STATE_MASK);}

最后保存状态时,由于childMeasureState是第一个字节是宽度的状态,第三个字节是高度的状态,所以再测量宽度高度时调用此方法,要对childState进行位运算


setMeasuredDimension

这个是用来保存当前view尺寸的,当我们使用自定义view并且覆写了onMeasure()方法时,不在最后调用这个方法,直接就报错了

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {        boolean optical = isLayoutModeOptical(this);        if (optical != isLayoutModeOptical(mParent)) { // layout_mode是LAYOUT_MODE_OPTICAL的情况很少出现,所以测量过程中涉及optical的,我们一般都可以直接忽略不计            Insets insets = getOpticalInsets();            int opticalWidth  = insets.left + insets.right;            int opticalHeight = insets.top  + insets.bottom;            measuredWidth  += optical ? opticalWidth  : -opticalWidth;            measuredHeight += optical ? opticalHeight : -opticalHeight;        }        setMeasuredDimensionRaw(measuredWidth, measuredHeight);}

直接调用了setMeasuredDimensionRaw()方法

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {        mMeasuredWidth = measuredWidth;        mMeasuredHeight = measuredHeight;        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;}

嗯,就是这一步,保存了measuredHeight和measuredWidth.不过,这里要说明一下,measuredHeight/Width高八位是状态,低二十四位才是真正的尺寸,这也就是View.getMeasuredWidth/Height和View.getMeasuredWidth/HeightAndState()方法的区别所在


getDefaultSize

    public static int getDefaultSize(int size, int measureSpec) {        int result = size; // size是view的默认尺寸        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;    }


getSuggestMinimunHeight/Width

以高度为例,代码如下

protected int getSuggestedMinimumHeight() {        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());}
涉及view的背景,mBackground是Drawable类的,它的getMinimumHeight()由不同的子类分别实现,反正就是返回最小的高度,然后再跟view本身的最小高度取最大值(view本身的最小高度不手动设置的话,就是0)


更多相关文章

  1. Android(安卓)Studio中JNI使用的一些出现的错误及方法
  2. AppCompatActivity与toolbar的结合
  3. Android基础知识】选项菜单、上下文菜单、子菜单的使用
  4. PowerManager源码
  5. Android(安卓)面试
  6. Android(安卓)Media player 报错Error(38,0)
  7. android view(2) Activity、Window、DecorView
  8. Android(安卓)SeLinux权限问题和解决方法
  9. Android(安卓)SharedPreference源码浅析

随机推荐

  1. Android传感器开发(下)
  2. Android仿微信长按菜单效果
  3. NDK/JNI01--NDK下载配置
  4. Android 学习——Android 菜单(OptionMen
  5. [Android入门]体系结构、UI组件使用、获
  6. 【Android】蓝牙快速开发工具包-入门级
  7. 整理 酷炫 Android(安卓)开源UI框架 引导
  8. 理解杀进程的实现原理
  9. AndroidStudio 3.0 加载Android support
  10. android防止屏幕关闭后后台服务停止运行