onMeasure的宽高生成解析

原创 2017年08月29日 21:06:17

在View的绘制流程中,onMeasure是负责测量控件的大小的

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

他的两个参数分别代表宽和高,接下来我们要看下这两个参数的意义以及他们究竟是如何生成的

首先这两个参数都是一个32位的int值,其中高2位代表了SpecMode,低30位则代表了SpecSize

SpecMode指的是测量模式

private static final int MODE_SHIFT = 30;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY     = 1 << MODE_SHIFT;
public static final int AT_MOST     = 2 << MODE_SHIFT;

共有三种模式,分别为UNSPECIFIED,EXACTLY 和AT_MOST,第一个一般不怎么用,EXACTLY 表明控件的宽高是确定的,AT_MOST表明父控件有指定的宽高,而该控件的宽高则不允许大于父控件的宽高,我们一般重写onMeasure都是针对于这种情况来设定控件的宽高

SpecSize就很简单了,就是该控件的宽高

我们接着看看MeasureSpec是如何生成的

布局的绘制起始于ViewRootImpl的performTraversals方法,而在performTraversals中测量流程最先始于performMeasure方法

int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

performMeasure调用了最顶层的View(也就是DecorView)的measure,最开始宽高的MeasureSpec来自于getRootMeasureSpec方法,先来说下参数:mWidth,mHeight是我们屏幕的宽高;而lp

WindowManager.LayoutParams lp = mWindowAttributes;

final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();

则是一个无参的LayoutParams,他的默认宽高都是MATCH_PARENT

public LayoutParams() {
      super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
      type = TYPE_APPLICATION;
      format = PixelFormat.OPAQUE;
}

接着看getRootMeasureSpec方法

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }

 public static int makeMeasureSpec(int size,int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

很简单,就是判断下lp的宽高类型,然后简单的相加,就生成了我们最初的MeasureSpec

接着来看看之后的MeasureSpec是如何生成的,在ViewGroup中有一个getChildMeasureSpec方法负责生成子View的MeasureSpec

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

还是先来说下这个方法的三个参数
spec:这个ViewGroup的MeasureSpec
padding:就是我们在xml文件定义的padding参数
childDimension:子View的LayoutParams,也就是我们写的WRAP_CONTENT,MATCH_PARENT这些

其实逻辑也很清楚,子View的MeasureSpec是由父控件的MeasureSpec和子View本身的LayoutParams共同决定的,因为MeasureSpec的Mode有三种,而LayoutParams的参数也可以分为三种,即WRAP_CONTENT,MATCH_PARENT和具体数值,所以上面有9种方式生成了子View的SpecMode和SpecSize,最后调用makeMeasureSpec方法生成了子View的MeasureSpec

最后来个表,表明子View的MeasureSpec的创建规则
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

EasyRTMP之H264 SPS解析宽高不正确导致播放时画面拉伸的问题修复(五)

EasyRTMPEasyRTMP是将H264流以及AAC流以RTMP协议推送到RTMP服务器上进行直播。EasyRTMP推送库中会从H264流中提取中SPS、PPS进行解析,开发的时候遇到过有些SPS...

h264 sps解析,提取宽高

sps 提取视频宽高

上传图片自定义宽高

  • 2013-11-29 16:52
  • 20KB
  • 下载

Android LayoutInflater深度解析 给你带来全新的认识(包含View的onMeasure方法)

本文出自:http://blog.csdn.net/lmj623565791/article/details/38171465 1、 题外话 相信大家对LayoutInflate都不陌生,...

获取屏幕宽高

FrameLayout measure过程源码Log全解析之三:onMeasure代码概览

在AS中阅读代码 我用到的ide是android studio(以下AS),
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)