之前分享了很多面试题,蛮多都自带正确答案,来几期易错系列,大家一起醒醒神。
今天来个简单的开胃一下。
这个知识点,我定义为在面试过程中答对不加分,答错扣分的题目,不过在我以前面试经历中,能完整说上来的同学不多。
我们一起来看看大家对这个知识的掌握程度吧。
在早期的博客的里面,很多时候,见到有如下的介绍:
- 如果你的
View
设置了match_parent
,则在onMeasure
中得到的测量模式为:EXACTLY
; - 如果设置了
wrap_conent
,则对应测量模式为:AT_MOST
; - 还剩下一个
UNSPECIFIED
大家不用管,不常用;
上述描述每句话都可以认为是错的。
那么今天我要搞清楚几个问题:
-
match_parent / wrap_conent
一定对应EXACTLY/ AT_MOST
吗 ? - 测量模式到底是由哪些因素确定的?
-
UNSPECIFIED
真的不常见吗?
1. match_parent和wrap_content
就一定对应MeasureSpec.EXACTLY
和MeasureSpec.AT_MOST
吗?
肯定不是。
为什么呢?
因为View
在measure
时,它的宽高MeasureSpec
完全是取决于父容器,父容器传的是什么它收到的就是什么。
如果这个父容器的onMeasure
方法里面写死了每个子View
的MeasureSpec
的Mode
为UNSPECIFIED
的话,那么无论你在xml
布局或者LayoutParams
中怎么设置宽高都好,最终子View
的onMeasure
收到的也是UNSPECIFIED
。
好吧,故意手动指定的不算。
就以正常的角度来看:
我们都知道,自定义ViewGroup
过程中,需要在onMeasure
里面对子View
进行测量。
在测量子View
时,往往会通过measureChild
、measureChildWithMargins
方法来完成(比如FrameLayout
、LinearLayout
、CoordinatorLayout
、ViewPager2
)。
或者调用ViewGroup
的静态方法getChildMeasureSpec
来直接获取目标子View
的MeasureSpec
,然后手动measure
(比如ScrollView
、NestedScrollView
、DrawerLayout
、TabLayout
、ConstraintLayout
)。
其实,measureChild
和measureChildWithMargins
里面也是会通过getChildMeasureSpec
方法来获取MeasureSpec
的,也就是说,上面提到的这些容器,在测量它们的子View
之前,都是先通过getChildMeasureSpec
方法来获取子View
的宽高MeasureSpec
,然后传给子View
的measure
方法的。
好,那我们现在来看看getChildMeasureSpec
方法里面做了什么:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
......
switch (specMode) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
......
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
......
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
......
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
......
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
......
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
......
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
......
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
......
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
......
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
可以看到:
在父容器的specMode为EXACTLY
时,一切正常(子View
尺寸指定为match_parent
或精确的dimen
值时,Mode = EXACTLY
,尺寸指定为wrap_content
则Mode = AT_MOST
);
当父容器specMode为AT_MOST
的时候,呵呵,可以看到,除了指定了dimen
值之外,无论设置为match_parent
或wrap_content
,Mode
最终都是会变成AT_MOST
;
如果父容器specMode
是UNSPECIFIED
的话,跟上面的逻辑差不多,都是会变成UNSPECIFIED
的,除非指定了精确的dimen
值;
所以,View
的onMeasure
方法中收到的宽高MeasureSpec
,不完全是由xml
布局中设置的宽高或LayoutParams
的宽高值决定的。
2. 有哪些因素影响着MeasureSpec的mode?
从刚刚的getChildMeasureSpec方法中可以看出,影响着View测量模式的因素主要是该View所属容器的测量模式。
也就是说,正常情况下(不是故意乱设置),View的测量模式是由:
**它自身的LayoutParams
设置的值 **+ 父容器的测量模式来决定的。
为什么大家都说MeasureSpec.UNSPECIFIED
不常见呢?
大家都觉得这个模式不常见,很可能就是因为在编写布局时,View
的宽高只能选择match_parent
、wrap_content
或者直接指定一个精确的尺寸,相对来说,MeasureSpec.UNSPECIFIED
就显得不太透明了,因为在日常开发中,如不需定制View
的话,基本上不会直接接触到。
3. MeasureSpec.UNSPECIFIED是不是真的不常见?
在日常定制View
时,确实很少会专门针对这个模式去做特殊处理,大多数情况下,都会把它当成MeasureSpec.AT_MOST
一样看待,就比如最最常用的TextView
,它在测量时也是不会区分UNSPECIFIED和AT_MOST
的。
不过,虽说这个模式比较少直接接触到,但很多场景下,我们已经在不知不觉中用上了,比如RecyclerView
的Item
,如果Item
的宽/高是wrap_content
且列表可滚动的话,那么Item
的宽/高的测量模式就会是UNSPECIFIED
。
还有就是NestedScrollView
和ScrollView
,因为它们都是扩展自FrameLayout
,所以它们的子View
会测量两次,第一次测量时,子View
的heightMeasureSpec
的模式是写死为UNSPECIFIED
的。
我们在自定义ViewGroup
过程中,如果允许子View
的尺寸比ViewGroup
大的话,在测量子View
时就可以把Mode
指定为UNSPECIFIED
。
好了,希望这次你彻底弄明白了自定义控件的测量模式相关知识。
另外也有人给我发了个图,说这个图就能说明白了,其实这个图也有一点点小问题:
我画圈的地方,这个值不一定是 0, 不过大多情况下 UNSPECIFIED
这个模式一般不在乎这个size
。
最后
今天就讲到这里,我的Android核心技术学习大纲,获取相关内容来我的GitHub一起玩耍:https://github.com/Meng997998/AndroidJX
对于进阶这条路而言,学习是会有回报的!
你把你的时间投资在学习上,就意味着你可以收获技能,更有机会增加收入。
分享我的Android学习PDF大全
这份Android学习PDF大全真的包含了方方面面了,内含Java基础知识点、Android基础、Android进阶延伸、算法合集等等
我的这份学习合集,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
Android学习PDF大全关注我看个人介绍,或者简信我获取
网友评论