measure()源码分析

/**
* host.measure()会 调 用 到View类 的 measure()函数,该函数然后回调onMeasure()。在一般情况下,
* host对象是一个ViewGroup实例,该ViewGroup会重载onMeasure(),当然如果host没有重载onMeasure(),
* 则会执行View类中默认的onMeasure()。在一般情况下,程序员需要在重载的onMeaSure()函数中逐一
* 对 所 包 含 的 子 视 图 执 行 measureO操 作 , 为 了 简 化 程 序 设 计 , ViewGroup类 内 部 提 供 了
* measureChildWithMargin()函数,该函数内部会进行一定的参数调整,然后再次调用子视图的measure()
* 函 数 , 这 就 又 调 用 到 onMeasure()。 如 果 子 视 图 是 一 个 ViewGroup实 例 , 则 应 该 继 续 调 用
* measureChildWithMarginO对下一层的子视图进行measure操作;如果子视图就是一个具体的View实例,
* 则 在 重 载 的onMeasures()函数内部就不需要再次调用measureChildWithMargins(),从 而 一 次 measure()
* 过程结束
*
* 在该函数定义中, final关键字说明,该函数是不能被重载的,即 View系统定义的这个measure框
* 架不能被修改。参数 widthMeasureSpec 和 heightMeaureSpec 分别对应宽和高的 measureSpec,measureSpec
* 是 一 个int型值,当父视图对子视图进行measure操作时,会调用子视图的meaSUre()函数,该参数的意
* 思是父视图所提供的measure的 “规格”,因为父视图最终为子视图提供的“窗口”大小是由父视图和
* 子视图共同决定的。 该值由高32位 和 低16位组成,其中高32位保存的是specMode,低 16位 为specSize。
* specMode有三种,分别如下。
* • MeasureSpec.EXACTLY: “确定的”,意思是说,父视图希望子视图的大小应该是specSize中指
* 定的。在一般情况下,View的设计者应该遵守该指示,将View的measuredHeigth和measuredWidth
* 设置成指定的值,当然,也可以不遵守。
* • MeasureSpec .AT_MOST: “最多”,意思是说,子视图的大小最多是specSize中指定的值。在一
* 般情况下, View的设计者应该尽可能小地设置视图的大小,并且不能超过specSize,当然,也
* 可以超过specSize。
* • MeasureSpec .UNSPECIFIED: “没有限制”,此 时 View的设计者可以根据自身的特性设置视图
* 的大小。
*/

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {

            // first clears the measured dimension flag
            mPrivateFlags &= ~MEASURED_DIMENSION_SET;

            if (ViewDebug.TRACE_HIERARCHY) {
                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
            }

            // measure ourselves, this should set the measured dimension flag back
            onMeasure(widthMeasureSpec, heightMeasureSpec);

            // flag not set, setMeasuredDimension() was not invoked, we raise
            // an exception to warn the developer
            if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
                throw new IllegalStateException("onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }

            mPrivateFlags |= LAYOUT_REQUIRED;
        }

        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;
    }

View.java 的onMeasure

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

ViewGroup.java measureChildWithMargins

/**
*  其 中 参 数parentHeightMeasureSpec是该child的父视图传递过来的measureSpec,参 数 heightUsed
     * 是在该measureSpec中,父视图已经使用了的高度。
     */
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        /**
         * 1) 调 用 child.getLayoutParmas()获得子视图的LayoutParams属性。该步虽然看起来只有一行代码,
         * 但其中却蕴涵了一组函数调用。首先来看该函数的返回值,在 View类 中 ,该函数的默认返回值是
         * ViewGroup.LayoutParams,而此处却被强制类型转换为ViewGroup.MarginLayoutParams类型。从程序的
         * 角度来讲,大家都知道,强制类型转换一般只能把子类强制转换为父类,而不能把父类强制转换为子类,
         * 除非该对象本身就是子类对象,否则会发生转换错误。而 此 处 MarginLayoutParams是子类,其父类是
         * LayoutParams,但代码中却把getLayoutParams()返回值强制转换为了 一个子类MarginLayoutParams对象,
         * 这是为什么呢?
         *这就要来看View对 象 的LayoutParams属性是如何被赋值的。创建一个View对象一般有两种方法,
         * 一种是使用XML文件静态描述,另一种是程序动态调用View类的构造函数,构造函数中指明所使用
         * LayoutParams参数。两种方法的本质是相同的,因此这里仅介绍第一种方法。
         *
         */
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        /**
         * 2 调用两次getChildMeasureSpec()函数,分别计算出该子视图的宽度和高度的Spec。前面曾经说
         * 过,子视图所占布局的大小取决于两个方面,一个是父视图提供的规格,另一个是子视图本身希望占用
         * 的大小,而这就是该函数参数的意义。
         */
        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()函数。子视图可以重载onMeasure()函数,并可调用setMeasureDimension()函数设置任意
         * 大小的布局,而这将成为该子视图的最终布局大小
         */
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

参 数 spec正是父视图提供的spec,源码中对应的变量是parentHeightMeasureSpec,这个值是int型 。
高 16位 代 表specMode, —共有三种模式,分别是AT—MOST、 EXACTLY及 UNSPECIFIED;低 16位
代 表 specSize,对于 非UNSPECIFIED模式,该值指父视图所允许的最大尺寸或者期望的理想尺寸。
参 数padding代表的是在父视图提供的spec尺寸中,已经使用的多少,源码中该值包含了 padding
填充、 margin空余,以及父视图所提供的已经使用的高度heightUsed:

m P a d d i n g T o p + m P a d d i n g B o t t o m + l p .t o p M a r g i n + l p .b o t t o m M a r g i n
+ h e i g h t U s e d

getChildMeasureSpec()中会从parent的 specSize中减去该padding,以便子视图使用已用的空间。
参 数 childDimension即为子视图期望获得的高度,其值来源于lp.height,这个值正是XM L文件中
andorid:layout_height对 应 的 值 , 可 能 是 一 个 具 体 的 值 , 也 可 能 是 MATCH_PARENT或者
WRAP_CONTENTo
getChildMeasnreSpecO的作用就是根据以上两个条件确定子视图的“测量规格”,注意,为什么这里
确定的不是最终子视图的尺寸而只是“测量规格” ?读者可以换一个角度来理解parentMeasureSpec和
lp.height这两个参数,前者实际上是指父视图可以提供的尺寸,而对一个应用程序而言,程序员必须负
责父视图所包含的所有子视图的排列关系,以达到一个优美的界面布局,因此,程序员才使用lp.height
指定期望该子视图的大小,而至于子视图到底有多大,还需要征询子视图本身的意思,所以该函数并不
能最终确定视图的尺寸。

 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 = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
    ```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值