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的创建规则
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xw13782513621/article/details/77688257

java生成和解析二维码

二维码在现实生活中无处不在,应用越来越广泛。那么小伙伴们想不想知道如何生成二维码,以及如何去解析二维码呢
  • 2018年01月16日 11:38

243_onMeasure方法设置宽高相等

onMeasure方法设置宽高相等 我们在测量的时候去判断然后控制宽高一致     @Override     protected void onMeas...
  • qq_33781658
  • qq_33781658
  • 2016-09-19 21:32:41
  • 281

自定义控件 继承View 使用OnMeasure定义控件宽高

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension...
  • u013597998
  • u013597998
  • 2015-12-21 12:30:54
  • 1075

在onMeasure中获取已测量的子控件的宽高

不能使用child.getWidth();因为这个用right - left得到的,在onMeasure中都还没布局控件树,那些位置参数都为0.只能通过child.getMeasureWidth();...
  • b1480521874
  • b1480521874
  • 2016-11-16 15:52:38
  • 611

Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)

详细研究Android中自定义控件中用到的measure/onMeasure方法是如何实现测量控件大小,如何传递参数的。...
  • cyp331203
  • cyp331203
  • 2015-04-13 18:02:25
  • 14681

android获取控件宽高和屏幕宽高

一、获取屏幕宽高 要获取屏幕宽高,我们可以先从android的界面构成了解 android的界面主要由三部分构成:1、状态栏 2、标题栏 3、内容区域 1、状态栏 2、标题栏 android中标...
  • littlewhitetree
  • littlewhitetree
  • 2016-06-15 13:46:10
  • 6956

LinearLayout 和 RelativeLayout onMeasure理解(一)

记录自己的理解,如果有错误 请大家指正 谢谢 人们说估计使用RelativeLayout而不是LinearLayout,因为LinearLayout比relativeLayout多测量一次,事实上是这...
  • sp_StudyAndoird
  • sp_StudyAndoird
  • 2017-03-29 21:27:45
  • 305

Android自定义View(三、深入解析控件测量onMeasure)

转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51468648 本文出自:【openXu的博客】 目录:onMeasure什么...
  • u010163442
  • u010163442
  • 2016-05-24 14:59:18
  • 25810

Android获取View的宽高与View.measure详解

Android获取View的宽高与View.measure详解
  • canot
  • canot
  • 2015-12-30 00:43:34
  • 2352

Android自定义控件之测量onMeasure

当Android原生控件无法满足开发需求时,需要自己来创造view,自定义控件。自定义控件分三步来完成:测量(onMeasure),布局(onLayout),绘制(onDraw)。今天主要介绍自定义流...
  • pihailailou
  • pihailailou
  • 2017-09-16 00:06:31
  • 447
收藏助手
不良信息举报
您举报文章:onMeasure的宽高生成解析
举报原因:
原因补充:

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