一看就懂的View测量过程介绍

来源:互联网 发布:尼泊尔少年 知乎 编辑:程序博客网 时间:2024/05/19 04:51

Android系统将屏幕上的UI组件抽象为View,并组织成树状结构,以便管理。一个View在屏幕上显示时,要先测量,从而确定自己的大小。网上各种讲解测量的文章,在我看来,都不够好,有的失于浅显无法揭露本质,有的堆砌源码,对初学者不友好。本文试图避免上述两个缺点,让初学者能够真正掌握View的测量过程。

要钱

view的测量过程,就是它与其父view沟通的过程,类似于小孩向家长要钱的过程,不同的小孩要钱的方式不一样。
儿子要钱的请求封装在LayoutParams中,主要指里面的width和height。父亲开的价封装在widthMeasureSpec和heightMeasureSpec中,里面包括一个mode和一个size。mode代表父亲的大方程度,size表示父亲最后给的钱数。为简便起见,本文分析width的确定过程。
1. 目标明确的小孩。
小孩:爸,我要十块钱。(width = 10)
父亲:(看看自己有没有)好,给你十块钱,拿去花吧儿子。 (mode = Exactly size = 10)
2. 贪心的小孩。
小孩:爸,你有多少钱我要多少钱。(width = match parent)
父亲:(看看自己还有8块)儿子,老爸总共只有8块钱了,全给你。 (mode = Atmost 或Exactly size = 8)

对于贪心的小孩,父亲给的钱既是最多的(Atmost),也是精确的(Exactly),具体用哪个mode都可以。最后的size就是父亲的所有钱。
3. 乖乖的小孩
小孩:爸,我不要多少钱,只要够我每天给小丽买零食就好了。(width = wrap content)
父亲:(心里嘀咕:这儿子真乖,可是到底该给他多少呢,算了,毕竟是亲生儿子,干脆有多少给多少吧)儿子,老爸所有的钱都给你,你看着花吧。(mode = Atmost size = 10)

另外,对于上述三种要钱的小孩,如果父亲是土豪,完全可以用一种方法回复儿子,那就是:随便花!此时的mode就是Unspecified。
小孩:(嘿嘿,会卖乖的小孩才有钱花呀)


花钱

子view通过上述三种方式之一,拿到了父view给的钱(即widthMeasureSpec) ,这些钱到底怎么花呢?也就是说,子view怎么通过父view给的widthMeasureSpec来确定自己的尺寸呢?过程如下:
1、数数父亲给的钱。 儿子:先来看看老爹最后给了多少钱。

(下面代码为伪代码)

mode = getMode(widthMeasureSpec);size = getSize(widthMeasureSpec);

2、看看自己至少得花多少钱。
儿子:先看看至少得花多少钱,调用我的getSuggestedMinimumWidth方法。
(下面代码为伪代码)

miniWidth = getSuggestedMinimumWidth()

3、确定花多少钱
儿子:根据我的至少得花的钱和父亲给的钱,确定我花多少钱。调用我的getDefaultSize方法。(下面代码为伪代码)

getDefaultSize(miniWidth,widthMeasureSpec){        //result就是儿子最后要花的钱。        int result = miniWidth;        int mode = getMode(widthMeasureSpec);        int size = getSize(widthMeasureSpec);        switch (mode) {         /*老爹说了,我不管你要多少钱,你自己看着花就是。作为一个懂事的儿子,必须学会给老爹省钱啊,只花最少的钱就是了。*/        case Unspecified:                   break;            /*老爹如果给我他所有的钱(Atmost),那我就全花了,(真 是 败 家 儿 子 !)如果给的钱是确定的(Exactly),那就给多少花多少吧。*/        case AT_MOST:        case EXACTLY:            result = size;            break;        }        return result;    }

4、开始花钱
儿子:好了,我要开始花钱了,好开心呦!!调用我的setMeasuredDimension方法。

setMeasuredDimension(result;//就是上面得到的result啦!

好了,上面的4个步骤统一到了一个onMeasure方法中。如下所示:(伪代码)

void onMeasure(int widthMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec));    }

学完上面的知识,我们发现这个儿子有点言行不一啊!当他向父亲提出要够花的钱时(wrap content),父亲把所有的钱都给了他(Atmost),结果这个败家儿子,把钱全花了(请看上面的getDefaultSize方法)。如果我们自定义一个儿子,让他言行一致,就要改写getDefaultSize方法,让他在父亲给的钱是Atmost时,不要直接把老爹的钱(size)全花了。具体怎么改写呢?
简单讲就是,对于父亲给的钱是Atmost mode时,给result 设定一个默认值。

getDefaultSize(miniWidth,widthMeasureSpec){        //result就是儿子最后要花的钱。        int result = miniWidth;        int mode = getMode(widthMeasureSpec);        int size = getSize(widthMeasureSpec);        switch (mode) {         /*老爹说了,我不管你要多少钱,你自己看着花就是。作为一个懂事的儿子,必须学会给老爹省钱啊,只花最少的钱就是了。*/        case Unspecified:                   break;            /*老爹如果给我他所有的钱(Atmost),那我就全花了,(真 是 败 家 儿 子 !)如果给的钱是确定的(Exactly),那就给多少花多少吧。*/        case AT_MOST://************改这里!!!!************************ //乖孩子不把父亲的钱花完,只花defaultsize的钱。                     result = defaultsize ;             break ;        case EXACTLY:            result = size;            break;        }        return result;    }

当然,还可以改写onMeasure 方法,聪明如你,一定已经知道怎么改了吧?

2 1
原创粉丝点击