原文链接 理解安卓的视图体系结构
当我们想要写一个页面的时候,通过一个Activity,然后调用其setContentView方法,把一个布局文件当作一个参数传递过去,然后一个页面就好了,但是除此之外,我们还需要与一些组件打交道,比如像Window,WindowManager,那么这些东西到底 与我们的页面布局有什么关系,今天就来学习一下,以便对整体窗口有个更清楚的认知。
布局是一颗View tree
先从一个最简单的例子出发,平时我们写一个页面,都从一个布局文件出发。这其实是在构建一个View tree,为啥一定是tree呢,因为我们的布局文件,无论有多么的复杂,都是从一个根(通常是一个ViewGroup对象)开始的,父布局里面再写子布局,比如这样的:
<LinearLayout id="app_root">
<TextView id="label"/>
<Button id="submit"/>
</LinearLayout>
这会形成一个树状结构:
| app_root
|- label
|- submit
作为一个开发者,写布局是我们再熟悉不过的了,主要就是用所熟悉的各种Layout和View一起来构建想要的页面。
所写的布局,最终会生成一颗View tree,是一个树状的数据结构,每一个节点都是一个View对象(ViewGroup和View)。因此,布局优化的一个是感觉重要的点就是要先减少View tree的深度(也即平时所说的减少布局的嵌套),再想办法减少广度(减少个数)。
那么,我们写的布局的父布局又是哪里呢?这就又涉及两个东西,一个叫做decorView和contentView的东西。
DecorView与ContentView
我们平常所见的屏幕窗口的根布局是一个叫做DecorView的东西,它是我们通常意义上整个屏幕的根节点,它包含了上面的Status bar和下方的Navigation bar,以及属于应用程序的中间部分。它的源码路径是frameworks/base/core/java/com/android/internal/policy/DecorView.java。它是一个真实的view,它是FrameLayout的子类。
它下面有一个id为android.R.id.content的FrameLayout,我们平时在Activity中调用setContent时所传过去的布局文件所生成的View tree都是添加在这个FrameLayout下面,所以,通常对于我们一个Activity来说,这个FrameLayout是直接意义上的根节点,我们所写的布局都是添加它下面的。
ContentView所引申出来的奇技淫巧
布局优化技巧
首先,一个是布局的优化技巧,可以减少View tree的层级:假如你写的布局中根节点也是一个FrameLayout,那么可以直接用merge节点,把子view全部都直接加挂到前面提到的系统创建的Activity的根布局上面。
<merge>
<Text />
<Button />
</merge>
这可以把View tree减少一个层级(深度减1)。
页面内即插即用的弹窗
每个Activity都被回挂在一个id是android.R.id.content的FrameLayout下面,利用这一点,可以做一些即插即用的弹窗,即插即用的意思是,不用写在布局里面,而且显示的时间是不固定的,可能很多时候都不显示,在某个特定的逻辑或者时间才显示。就好比某些电商特定节日的弹窗一样,这种东西,一年也显示不了几回,如果直接添加在布局里面(哪怕你用ViewStub),不够优雅,毕竟不是常规逻辑下会出现的页面,这时可以利用content来做一些即时弹窗:
FrameLayout container = activity.findViewById(android.R.id.content);
View pop = <create or inflate your own view>;
container.addView(pop);
只要你能获得到Activity的实例(这个并不难),那么就可以非常优雅的添加弹窗,逻辑代码和布局文件都会相当独立,甚至可以用插件形式来异步加载。再进一步,如果 添加一个WebView,那么就可以做得更加的前端化,实时化和定制化,好多电商的弹窗就是这么干的。
Window与WindowManager
作为应用开发者,我们看一个View tree其实就是一坨布局,这是站在一个非常小的角度去看的,但如果站在整体系统架构角度来看的话,就会发现应用程序所在的view tree仅是系统可视化窗口架构中的末端,View只是用来构建视图的基本砖块而已。对于整体View