View与ViewGroup绘制原理解析(二),2021Android开发面试解答之设计模式

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进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**

跨平台开发:Flutter.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值