【Android】自定义 View 系列-MeasureSpec

【Android】自定义 View 系列- 绘制流程 一文中,在测量过程中,是通过 一定的规则 得出最后我们测量的宽高,然后通过 setMeasuredDimension() 保存结果。

那么这里说到的规则主要就是---MeasureSpec 和 LayoutParams

MeasureSpec 是??

//源码描述
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height. 
* A MeasureSpec is comprised of a size and a mode. There are three possible modes:
* UNSPECIFIED
* The parent has not imposed any constraint on the child. It can be whatever size it wants.
* EXACTLY
* The parent has determined an exact size for the child. The child is going to be given  
* those bounds regardless of how big it wants to be.
* AT_MOST
* The child can be as large as it wants up to the specified size.
*/
提取主要内容:
1.MeasureSpec 父 View 对子 View 的宽高要求。
2.MeasureSpec 包含了两个信息 模式(mode)和大小(size)
3.一共有三种模式:
    UNSPECIFIED- 任意大小(It can be whatever size it wants )-例如 recyclerview 子View 的大小是超过父View 的大小的
    EXACTLY - 精确值(regardless of how big is wants to be, an exact size )、
    AT_MOST - 在父View 的限制范围中任意大小(as large as it wants up to the specified size).

但是,对于我们开发者来说,我们知道一个 View 的大小,不是应该由我们通过 layout_width 和 layout_height 决定的吗?

对,也不对。事实是,在对于任意一个View (包括ViewGroup),最终的大小是由父 View 的要求(MeasureSpec)和子View 自己的需求(LayoutParams-layout_width & layout_height)决定的。具体是如何决定的呢?

/**
* @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;
    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 them 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);
}

(上面代码逻辑很简单,源码的注解也很贴心了,请各位耐心读完。然后再看下面的总结表格,切忌死记硬背下面的表格)

总结为表格,其中:childDimension(子View 自身大小)、size(父view剩余空间)

EXACTLY

AT_MOST

UNSPECIFIED

具体值

(即 childDimension ≥ 0)

模式:EXACTLY

大小:childDimension

模式:EXACTLY

大小:childDimension

模式:EXACTLY

大小:childDimension

match_parent

模式:EXACTLY

大小:size

模式:AT_MOST

大小:size

模式:UNSPECIFIED

大小:View.sUseZeroUnspecifiedMeasureSpec ? 0 : size

wrap_content

模式:AT_MOST

大小:size

模式:AT_MOST

大小:size

模式:UNSPECIFIED

大小:View.sUseZeroUnspecifiedMeasureSpec ? 0 : size


今日疑问:为什么在一次事件序列中 onInterceptTouchEvent() 返回 true 后就不会再次执行onInterceptTouchEvent() 了,而在下一次事件序列中又会再次触发吗?

这个问题,我暂时没有找到切确的答案。截止目前研究,暂时认为是 mGroupFlags 标志被改变后,导致进入 onInterceptTouchEvent() 不再触发,并且在 UP/CANCEL/HOVER_MOVE 和下次事件序列开始(即 DOWN) 会调用 resetTouchState() 重置状态。这样又可以触onInterceptTouchEvent()。

对于 MeasureSpec 的内容和 上述疑问欢迎大家评论区交流~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q-CODER

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值