Android自定义 View 系列-MeasureSpec
Posted Q-CODER
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 的内容和 上述疑问欢迎大家评论区交流~
以上是关于Android自定义 View 系列-MeasureSpec的主要内容,如果未能解决你的问题,请参考以下文章
Android自定义 View 系列-MeasureSpec