return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供了打包和解包的方法。SpecMode和SpecSize也是一个int值,一组SpecMode和SpecSize可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式得出其原始的SpecMode和SpecSize,需要注意的是这里提到的MeasureSpec是指MeasureSpec所代表的int值,而不是MeasureSpec本身。
SpecMode有三种分类:
-
UNSPECIFIED: 父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
-
EXACTLY: 父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的match_parent和具体的数值这两种模式
-
AT_MOST: 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不用View的具体实现,对应LayoutParams中的wrap_content。
MeasureSpec和LayoutParams的对应关系
MeasureSpec不是唯一由LayoutParams决定的,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽高。另外,++对于DecorView和普通View来说,MeasureSpec的转换过程略有不同。++ 对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同决定;对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。MeasureSpec确定之后,onMeasure就可以确定View的测量宽高。
MeasureSpec遵循的规则:
-
LayoutParams.MATCH_PARENT:精确模式,大小就是窗口或父容器的大小;
-
LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口大小;
-
固定大小(比如100dp):精确模式,大小为LayoutParams中指定的大小。
ViewGroup的measureChildWithMargins方法
/**
-
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);
}
上述方法会对子元素进行measure,在调用子元素的measure方法之前,会先通过getChildMeasureSpec方法来获取子元素的MeasureSpec。显然,子元素的MeasureSpec的创建和父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和View的margin和padding有关,具体情况如下:
/**
-
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;
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:
总结
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的 点击我的GitHub免费获取。
还有免费的高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。
8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)免费获取。
还有免费的高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**