博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
View在测量时的MeasureSpec由什么决定?
阅读量:7075 次
发布时间:2019-06-28

本文共 7843 字,大约阅读时间需要 26 分钟。

 

我们都知道系统要确定View的大小,首先得先获得MeasureSpec,再通过MeasureSpec来决定View的大小。

MeasureSpec(32为int值)由两部分组成:

SpecMode(高2位):测量模式。

SpecSize(低30位):某种测量模式下的规格大小。

 

SpecMode有3类:

UNSPECIFIED: 父容器不对view做大小限制,一般用于系统内部,表示一种测量状态。

EXACTLY:精确模式。对应于:LayoutPrams中的match_parent和具体数值。

AT_MOST:最大值模式。对应于LayoutParam中的wrap_content模式。

 

那么问题来了,这个MeasureSpec又是由什么决定的呢?我们从源代码里面入手。

ViewGroup里面有一个方法,叫做measureChildWithMargins,用来测量子view的大小的。

/**     * Ask one of the children of this view to measure itself, taking into     * account both the MeasureSpec requirements for this view and its padding     * and margins. The child must have MarginLayoutParams The heavy lifting is     * done in getChildMeasureSpec.     *     * @param child The child to measure     * @param parentWidthMeasureSpec The width requirements for this view     * @param widthUsed Extra space that has been used up by the parent     *        horizontally (possibly by other children of the parent)     * @param parentHeightMeasureSpec The height requirements for this view     * @param heightUsed Extra space that has been used up by the parent     *        vertically (possibly by other children of the parent)     */    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);    }

可以看出,在代码里,会先获取childWidthMeasureSpec和childHeightMeasureSpec,然后再测量子元素 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

我们关键是要看这两个MeasureSpec是怎么获取到的,通过方法getChildMeasureSpec(parentWidthMeasureSpec,

                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);我们可以看出MeasureSpec的获取不但与子元素本身的LayoutParam有关,还与父容器的MeasureSpec有关,当然,也与它的padding,margin有关。
我们进去看看,这部分代码有点长,不过里面的逻辑很简单,我们直接在源代码里面通过加注释来分析。

/**     * Does the hard part of measureChildren: figuring out the MeasureSpec to     * pass to a particular child. This method figures out the right MeasureSpec     * for one dimension (height or width) of one child view.     *     * The goal is to combine information from our MeasureSpec with the     * LayoutParams of the child to get the best possible results. For example,     * if the this view knows its size (because its MeasureSpec has a mode of     * EXACTLY), and the child has indicated in its LayoutParams that it wants     * to be the same size as the parent, the parent should ask the child to     * layout given an exact size.     *     * @param spec The requirements for this view     * @param padding The padding of this view for the current dimension and     *        margins, if applicable     * @param childDimension How big the child wants to be in the current     *        dimension     * @return a MeasureSpec integer for the child     */    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; //子元素的specSize        int resultMode = 0; //子元素的specMode        switch (specMode) {        // Parent has imposed an exact size on us        case MeasureSpec.EXACTLY: //如果父容器的测量模式为EXACTLY            if (childDimension >= 0) { //如果子元素的LayoutParam为具体数值>=0                resultSize = childDimension; //那么子元素的specSize就是childDimension                resultMode = MeasureSpec.EXACTLY; //那么子元素的specMode是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                 //这里View.sUseZeroUnspecifiedMeasureSpec一直都是false,因此resultSize==0;                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);    }

由以上的分析可以看出子元素的MeasureSpec的获取不但与子元素本身的LayoutParam有关,还与父容器的MeasureSpec有关,当然,也与它的padding,margin有关。

其实我们可以得出下面的结论:

 

 

            parentMeasureSpec

 

childLayoutParam

EXACTLY

AT_MOST

UNSPECIFIED

具体数值

EXACTLLY

childSize

EXACTLY

childSize

EXACTLY

childSize

Match_parent

EXACTLY

parentSize

AT_MOST

parentSize

UNSPECIFIED

0

Wrap_content

AT_MOST

parentSize

AT_MOST

parentSize

UNSPECIFIED

0

 parentSize是父容器的剩余空间。

从上面的表格也可以看出,当我们直接继承view来实现自定义控件的时候,需要重写onMeasure方法并设置wrap_content时候的自身大小,不然使用wrap_content得时候就相当于使用match_parent。我们可以用下面的模板:

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSpecMode=MeasureSpec.getMode(widthMeasureSpec);        int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);        int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);        int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);        if (widthSpecMode==MeasureSpec.AT_MOST && heightSpecMode==MeasureSpec.AT_MOST)        {            setMeasuredDimension(100,100);        }        else if (widthSpecMode==MeasureSpec.AT_MOST)        {            setMeasuredDimension(100,heightSpecSize);        }        else if (heightSpecMode==MeasureSpec.AT_MOST)        {            setMeasuredDimension(widthSpecSize,100);        }    }

上面代码中的100是我们自己设置的数值,你也可以用其他数值,根据自己需要。

 

以上便是对MeasureSpec由何决定的分析。

转载于:https://www.cnblogs.com/tangZH/p/7040785.html

你可能感兴趣的文章
滚动条
查看>>
20. Valid Parentheses
查看>>
cssReset - css初始化
查看>>
mybatis generator Date类型时间丢失
查看>>
python 基础 4.5 用函数实现九九乘法表
查看>>
python 基础 9.2 mysql 事务
查看>>
利用表格分页显示数据的js组件datatable的使用
查看>>
shell编程系列13--文本处理三剑客之sed利用sed追加文件内容
查看>>
ocp 043 第八章:监事和管理内存
查看>>
【算法学习笔记】83.排序辅助 动态规划 SJTU OJ 1282 修路
查看>>
html学习记录之表格、表单基础
查看>>
前端性能优化(十)
查看>>
在龙芯小本上安装Debain8.10
查看>>
AppCode cocoapods install 慢
查看>>
7 个 JavaScript “特性”
查看>>
Mac 命令行美化
查看>>
<c:if test=""></c:if>如何判断空(使用例子)
查看>>
我的Android进阶之旅------>Android【设置】-【语言和输入法】-【语言】列表中找到相应语言所对应的列表项...
查看>>
PDF 补丁丁 0.6.0.3288 版发布(修复“合并文件”功能的文件夹文件排序问题)
查看>>
mybatis 学习总结笔记Day2
查看>>