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

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

ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

自定义视图详解。MeasureSpec,onMeasure,measureChildren等方法源码分析。
  • a396901990
  • a396901990
  • 2014年07月17日 00:27
  • 18093

Android自定义View-------为什么重写onMeasure()以及怎么重写

android自定义View,为什么重写onMeasure()以及怎么重写.
  • CHZiroy
  • CHZiroy
  • 2015年03月08日 17:00
  • 9856

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

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

android中对View的onMeasure()方法的理解

在android开发中,很多人对自定义View是望而生畏,我也一样,但这又是向高级进阶的必经之路,主要是对View里面的很多方法不知道怎么理解,其中一个就是onMeasure()方法,网上有很多这样解...
  • lovexieyuan520
  • lovexieyuan520
  • 2016年01月31日 15:44
  • 12309

自定义View中为什么需要重写onMeasure()方法?

不实现OnMeasure()方法的时候 首先自定义一个简单的view: public class myView extends View { public myView(Context cont...
  • superharder
  • superharder
  • 2014年10月30日 15:54
  • 1361

自定义控件:onMeasure 方法和测量原理的理解

前言众所周知,自定义 ViewGroup 中这几个方法非常重要:onMeasure, onLayout。初学者学习自定义 View 时,想必对 onMeasure 比较困惑,onMeasure 是什么...
  • heshiweij
  • heshiweij
  • 2016年03月20日 10:04
  • 1708

自定义控件View之onMeasure调用时机源码分析

先上测试代码:MainActivity.javaimport android.app.Activity; import android.os.Bundle; import android.util.L...
  • hty1053240123
  • hty1053240123
  • 2017年08月01日 17:56
  • 228

Android开发之自定义控件(一)---onMeasure详解

坚持。。。
  • dmk877
  • dmk877
  • 2015年11月06日 09:27
  • 9498

(总结篇)Android 牛不牛?决定于自定义View控件(一)——view绘制流程(onMeasure,onLayout,onDraw)

由于Android系统提供的控件比较多,因此我们只能挑一个比较有代表的控件进行分析。这个比较有代表性的控件便是TextView,其它的一些基础控件,例如Button、EditText和CheckBox...
  • proeshubiao
  • proeshubiao
  • 2015年03月28日 20:31
  • 719

Android自定义控件系列八:详解onMeasure()(二)--利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

上一篇文章详细讲解了一下onMeasure/measure方法在Android自定义控件时的原理和作用,参看博文:Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(...
  • cyp331203
  • cyp331203
  • 2015年04月14日 13:50
  • 9826
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:onMeasure的宽高生成解析
举报原因:
原因补充:

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