问题:Android是如何确定View大小的?
width和height是独立的,可以分开考虑,所以我们只关注width;
假设:
1)我们先自定义三个View A,B,C,全部继承自LinearLayout;
2)我们的测试机的屏幕宽度是1080;
实验1, 如果全部跟着父控件走:
则执行顺序是:
A EXACTLY 1080 (A的上级是1080宽度,而A的愿望是match_parent, 所以上级给A的spec就是EXACTLY 1080)
B EXACTLY 1080 (A的spec是EXACTLY 1080, B自己的愿望是match_parent,所以A给B的spec就是EXACTLY 1080)
C EXACTLY 1080 (B的spec是EXACTLY 1080, 而C自己的愿望是match_parent, 所以B给C的spec就是EXACTLY 1080)
C.width = 1080 (C的spec是EXACTLY 1080, 于是宽度就确定为1080)
B.width = 1080 (C的spec是EXACTLY 1080, 于是宽度就确定为1080)
A.width = 1080(C的spec是EXACTLY 1080, 于是宽度就确定为1080)
spec从上往下传递,而最终size的确定顺序是从下往上的;
实验2,如果全部跟着子控件走:
则执行顺序是:
A AT_MOST 1080(A的上级是1080的,而A想包含内容,所以上级就规定A最多1080宽)
B AT_MOST 1080 (A最多1080,而B想包含内容,所以A规定B最多1080宽)
C AT_MOST 1080 (B最多1080,而C想包含内容,所以B规定C最多1080宽)
C.width = 0 (C的spec是AT_MOST 1080, 而C作为LinearLayout的子类,由于没有内容,所以决定取0,其实这儿C只要不超过1080,可以随便取值的)
B.width = 0 (B的spec是AT_MOST 1080, 而C已经确定为0,为了wrap C,B可以取0到1080之间的任意值,这儿实际取值0,注意B既考虑了第一遍从上而下传过来的spec,也考虑了child的宽度)
A.width = 0 (A的spec是AT_MOST 1080, 而B已经确定为0,为了wrap B,A可以取0到1080之间的任意值,这儿实际取值0)
实验3,parent让child定,child让parent定,简单点,2级就能说明问题:
则执行顺序是:
A AT_MOST 1080(A的上级是1080的,而A想包含内容,所以上级就规定A最多1080宽)
B AT_MOST 1080 (A最多1080,而B想跟parent也一样大,由于A此时只知道不超过1080,并没有确定具体的值,所以A也只能规定B最多1080)
B.width = 0 (B的spec是AT_MOST 1080, 所以可以在0到1080任意取值,实际取值0)
A.width = 0 (A的spec是AT_MOST 1080, 而B已经确定为0,为了wrap B,A可以取0到1080之间的任意值,这儿实际取值0)
规律:
1)spec从上往下传递,而最终size的确定顺序是从下往上的;
2)第一遍是从上往下走,每个parent根据自己的spec,结合child的parameter,计算出给child的spec;
第二遍从下往上确定大小,每个child应该尊重parent给的spec,当然了,如果你非要违反,也没问题,等轮到parent决定大小的时候,可以根据child的实际大小见机行事,比如当child超过自己的时候引入滚动;
每个parent在决定大小的时候,既要考虑第一轮传下来的spec,也要考虑child已经确定好的大小,至于如何根据child的大小行事,就看你怎么实现onMeasure()方法 了,
通常默认的onMeasure()实现不是你想要的效果;
3)wrap_content应该了解为大于等于,而不是等于,只要能包含child就可以;
4)UNSPECIFIED在我们的例子里都没出现,这种spec估计是用在ScollView或者ListView里(height上),parent完全不给child任何的约束,child想多大就多大;