FrameLayout measure过程源码Log全解析之六:onMeasure第一部分之MeasureSpec类mode和size

上篇我们讲了MeasureSpec类如何通过bit-mask高效地储存mode和size

本篇我们来说说到底什么是MeasureSpec.EXACT,什么又是WRAP_CONTENT,它们的关系又是如何。

ViewGroup.LayoutParams类

在XML中看到的WRAP_CONTENT等实际属于LayoutParams类,这个类是FrameLayout的内部静态类,父类是ViewGroup.MarginLayoutParams,而他的父类是ViewGroup.LayoutParams, 所以

FrameLayout.LayoutParams extends ViewGroup.MarginLayoutParams extends ViewGroup.LayoutParams
先看最顶端的父类ViewGroup.LayoutParams,他有三个常量,三个变量
public static final int FILL_PARENT  = -1              // 旧版的MATCH_PARENT
public static final int MATCH_PARENT = -1              // 大小等于parent view - padding
public static final int WRAP_CONTENT = -2              // 和内容大小 + 自己的padding
public int width                                       //  确切的宽度
public int height                                      //  确切的高度
public LayoutAnimationController.AnimationParameters layoutAnimationParameters;  // 用来动画的,这个和onMeasure无关,先不看
四个constructor
public LayoutParams(Context c, AttributeSet attrs)     // 读取xml 
public LayoutParams(int width, int height)             // 直接给长宽
public LayoutParams(LayoutParams source)               // 复制已有的layoutParam
LayoutParams()                                         // 空白函数,被MarginLayout的constructor调用了一次,不明白为什么要这个
一个设置长宽的便捷函数
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)  // MarginLayout会调用这个函数,确保设置长宽
一个设置方向的函数
public void resolveLayoutDirection(int layoutDirection)  // 也是被MarginLayout调用的

两个debug函数,一个方便打印的函数,就不列出了。

可以看到这个最基础的类实际没有做太多工作,只是基本结构给建好了。

ViewGroup.MarginLayoutParams类

这个类集中对margin进行了处理。这个类比较复杂,值得专门开一个章节去讲,onMeasure和它关系不大,这里就不仔细讲了。

FrameLayout.MarginLayoutParams类

这个类在处理了margin(MarginLayoutParams)的基础上,加入了gravity。gravity实际是所有child view对齐的方向。这个类只有几个不同的constructor,分别是:

public LayoutParams(Context c, AttributeSet attrs)  // 从xml读取属性,相比ViewGroup.LayoutParams的对应构建函数,加入了读取gravity的部分
public LayoutParams(int width, int height)          //  直接设置长宽, gravity 默认是 -1
public LayoutParams(int width, int height, int gravity) // 直接设置长宽和重力
public LayoutParams(ViewGroup.LayoutParams source)      // 复制已有ViewGroup.LayoutParams
public LayoutParams(ViewGroup.MarginLayoutParams source)// 复制已有MarginLayoutParams
public LayoutParams(LayoutParams source)                // 复制已有FrameLayout.LayoutParams
因为FrameLayout.LayoutParams是继承ViewGroup.LayoutParams所以也有WRAP_CONTENT等成员常/变量。具体而言有三种情况

FILL_PARENT/ MATCH_PARENT
WRAP_CONTENT
具体width/height
所以width和height如果是-1或者-2就是前两种,如果是>=0的值则是第三种。

而MeasureSpec类的三种情况则是

EXACTLY
AT_MOST
UNSPECIFIED

接下来我们看看这6个变量之间的关系。

常/变量的含义及关系

我们继续阅读FrameLayout.java的onMeasure方法,会看到它对每个child view都会调用measureChildWithMargins方法来测量大小。


measureChildWithMargins

我们来看这个方法最后实际是调用了child的measure方法。child的measure方法实际是把已经测量好的childWidthMeasureSpec和childHeightMeasureSpec做了cache, 然后调用了child的onMeasure方法。

1. 实际测量的过程在getChildMeasureSpec这个方法。

2. 注意传入参数,2为父类的measureSpec

3. 为各种padding和margin的和

4. 为lp.width,即child view的宽度值,可能是WRAP_CONTENT, MATCH_PARENT或是具体值


getChildMeasureSpec

再来看getChildMeasureSpec方法的第一部分, 注意,spec是parent的measureSpec, childDimension是child的layoutParam类型(WRAP_CONTENT/MATCH_PARENT/具体值)

1. 把父类的mode和size拿出来

2. 把parent view的大小减去child view的padding和margin,要求>=0


接下来判断resultSize和resultMode的值。resultSize和resultMode会组成child的measureSpec。

首先判断parent的specMode,然后判断child的layoutParam类型,由此决定child 的measureSpec。这样两者的关系,大致清楚了。

比如:

1. 如果parent measureSpec的Mode是EXACTLY

2. 如果child的layoutParam类型是具体数值

3. 那么child的MeasureSpec中size = layoutParam的size, mode等于EXACTLY


总结一下

关系

MeasureSpec是parent对child的要求,layoutParam是child对自己的愿望,child的MeasureSpec(child 对自己的child的要求)由parent的要求和child的愿望决定。

含义

(A是parent, B是child,第三句是结果,注意parent的大小是减去了padding和margin的)

第一组

A"给我具体大小",B”我要具体大小“,B的具体大小

A“给我具体大小”,B"我要和你一样“,和A一样的大

A"给我具体大小",B"最多和你一样",最多和A一样大

第二组

A"最多和我一样大",B”我要具体大小“,B的具体大小

A"最多和我一样大",B"我要和你一样",最多和A一样大

A"最多和我一样大",B"最多和你一样",最多和A一样大

第三组

A"不知道啊",B”我要具体大小“,B的具体大小

A"不知道啊",B"我要和你一样“,不知道啊先等于0吧

A"不知道啊",B"最多和你一样",不知道啊先等于0吧

用法



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值