快速理解android View的测量onMeasure()与MeasureSpec

原创 2016年06月01日 10:45:31

笔者之前有一篇文章已经使用onMeasure()解决了listview与scollview的显示冲突问题,博客地址如下:

onMeasure简单方法 完美解决ListView与ScollView冲突问题!


在此就针对View的测量以及onMeasure()涉及的几个问题做一个详细解释:

一、MeasureSpec的概念:

MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供了打包和解包的方法。SpecMode和SpecSize也是一个int值,一组SpecMode和SpecSize可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode和SpecSize。

读者只要记住以下一句话即可:

MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。


二、SpecMode的三种模式:

1. EXACTLY

当我们将控件的“layout_width”属性或者“layout_height”属性指定为具体数值时,比如“android:layout_width="200dp"”,或者指定为“match_parent”时,系统会使用这个模式。

2. AT_MOST

当控件的layout_width”属性或者“layout_height”属性设置为“wrap_content”时,控件大小一般会随着内容的大小而变化,但是无论多大,也不能超过父控件的尺寸。

3. UNSPECIFIED

表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,一般在绘制自定义View的时候才会用到。


三、View的测量到底和什么有关呢?

要探其原理,首先要和大家说明一点,一个View只需要MeasureSpec确定,那么在onMeasure中就可以测量它的宽高,所以我们可以将问题直接转化成“一个View的MeasureSpec是如何确定的呢?”


普通的View的measure过程由VIewGroup传递而来,此处我们根据源码来做一个解释,先看一下ViewGroup中的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);
    }
从中,我们可以看到一个view的宽高,都是通过getChildMeasureSpec()这个方法获得的,那么这里面又是怎么实现的呢?我们不妨Control+左键点进去看一下,代码如下:

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;
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

代码比较长,读者可以比较焦急——不用害怕,我们不需要完全理解它的原理,我们只需要知道View的测量是如何实现的就行了。

看到源码方法中的三个参数,并且比较measureChildWithMargins()方法中传递给getChildMeasureSpec()三个值,我们很快就可以理解,一个View的测量过程是由父布局的MeasureSpec和该View的LayoutParams决定的。


读者可以看measureChildWithMargins()中如下代码:

final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);

要测量子部局的宽度的MeasureSpec,需要传入3个参数:

第一个参数:父布局的宽度的MeasureSpec

第二个参数:子部局的padding值,子部局的LayoutParams的Margin值

第三个参数:子部局的LayoutParams的宽度


如果对View的测量过程由更深入的求知欲的,推荐读者可以自己看一下源码。

版权声明:本文为博主许佳佳原创文章,转载请务必注明出处。

MeasureSpec的简单说明

MeasureSpec在onMeasure过程中的应用在源码中的体现

Android学习笔记-MeasureSpec的理解

MeasureSpec字面意思为测量的规格,他决定了view的测量过程 接下来探讨几个问题来学习MeauseSpec 1. MeasureSpec的构成 2. 如何创建MeasureSpec 3...

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

自定义视图详解。MeasureSpec,onMeasure,measureChildren等方法源码分析。

<android>MeasureSpec的彻底讲解

对于初学者来说,想搞懂android中view出现在屏幕上三个阶段(测量[onMeasure],布局[onLayout]和绘制[onDraw])是很困难的,我也是一名android初学者,这些东西老师...
  • Mod_J
  • Mod_J
  • 2015年09月19日 23:57
  • 607

解析View中的MeasureSpec

MeasureSpec是View中的一个内部类,封装了父View传给子View的布局需求: public static class MeasureSpec { private stat...

笔记83--自定义view--onMesure、MeasureSpec详解

转自:http://blog.csdn.net/a396901990/article/details/36475213
  • mwj_88
  • mwj_88
  • 2014年11月17日 17:10
  • 4016

获取view的高度和宽度(在onCreate方法中)

大家都知道在onCreate方法中一般没办法直接调用view.getHeight方法来获取到控件的高度以及其他一些信息,那我们如何取得控件的高度值呢? 大家可以采用以下的方法哈~ int w...

MeasureSpec介绍及使用详解

一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。它有三种模式:UNSPECIFIED(未...

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

Android获取View的宽高与View.measure详解
  • canot
  • canot
  • 2015年12月30日 00:43
  • 1458

Android开发 MeasureSpec介绍

搬家后的博客链接: IT客栈 www.itkezhan.org 在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:快速理解android View的测量onMeasure()与MeasureSpec
举报原因:
原因补充:

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