之前看郭霖文章讲LayoutInflater 的时候,有讲到在加载的layout中最外层布局设置绝对宽高是无效的。我们在Activity中setContenView()的时候,其实质也是利用的LayoutIflater 加载的Activity布局。而这时设置的绝对宽高是有效的。是因为在setContentView()的时候,系统会默认在外面包一层FrameLayout。郭霖还用log把当前Activity布局的父布局打印了出来。
当时看到这个心里面就很疑问,为什么要在外面默认加一层父布局呢?后来看到Android群英传,才知道,原来根本不是什么默认加了一层父布局,而是和Activity界面的架构有关。下面就先说下Activity的视图架构。
先上图:
就是这张图,Activity的UI架构图(图是网上找的)。从图中可以清楚的看出,Activity的UI结构。其实每个Activity中都包含一个Window对象,通常,Android中的Window是由PhoneWindow实现的。而PhoneWindow又将一个DecorView设置为整个窗口的根View(DecorView是一个ViewGroup)。
DecorView里面又有两个View,一个是用作title或者导航栏的,另外一个是ID为content的FrameLayout用来装我们加写的Xml文件布局的View
。这也就是我们给Activity设置布局的方法命名为setContentView的原因。我们如果在AndroidStadio中创建一个Activity,setContentView,我们的加载布局的只是title下面那一块矩形区域,然后上面一般是显示我们App名称的一个title。
在我第一次写一个Activity的时候,就很想把Activity上面的title去掉。网上终于查到用
requestWindowFeature(Window.FEATURE_NO_TITLE);但是没效果。当时那个郁闷,于是又继续搜,终于给我发现了,原来requestWindowFeature(
Window.FEATURE_NO_TITLE
)这个方法要在setContentView,之前调用。于是照做了,靠谱。然而当时并不懂为什么要这么搞,现在就很明显了。View的加载肯定是现有父布局,然后才加载子view的嘛,这个从很多方面都可以看出来,比如findViewById时要用根布局或者父布局的findViewById方法,还有一些子View定义在父布局里的属性,像居中,距离边框多远之类的,没有父布局的话,怎么搞。明白了这个,我们就很容易理解了。当我们setContentView之后,我们的Xml文件都加载了,那他的父布局的FrameLayout肯定加载好了,和他同深度的title的那个View也加载好了,所以再去设置
Window.FEATURE_NO_TITLE属性也就没用了
,所以必须把设置窗口属性的代码放在setContentView之前。窗口在setContentView之前把title干掉,然后整个父布局里就剩下setContentView里的布局了,自然占满整个窗口了。
下面看Activity的视图树:
不再反复说了,总结下:Activity不管视图显示方面的事,而是把视图管理交给了Window,Window是一个抽象类,他的实现是PhoneWindow。也就是Activity里面有个属性是一个PhoneWindow。然后这个PhoneWindow又是有一个根布局ViewGroup叫做DecorView,这个DecorView又有两个ViewGroup,就如上图了。。。其实Android就是通过这种委托思想来处理视图显示的事的。当然至于onResume等Activity生命周期变化时什么时机这些View显示,什么时机不可见,不是本篇讨论的内容了。上面只是说明要显示时,加载的顺序,和加载出来以后的视图结构。
结束,拜拜!