Activity 中 setContentView 的深入思考

版权声明:本文为“剑西”原创文章,转载需注明出处! https://blog.csdn.net/mabeijianxi/article/details/73608645


请尊重原创,转载请注明出处:http://blog.csdn.net/mabeijianxi/article/details/73608645


先看张图不说话:






往事得从 Activity 的 Window 创建说起,话说啊在启动一个 Activity 的时候经过一系列调用,终于到达了  ActivityThread 的 performLaunchActivity 方法,里面通过类加载器加载了 Activity 实例后会调用其 Activity 的 attach 方法,下面是 performLaunchActivity 方法里面的两个片段,前后省略数以亿计的代码:





然后在 attach 这个方法里面 通过 PolicyManager.makeNewWindow 最终会 new 一个 PhoneWindow 对象并且返回:




当我们调用 setContentView 设置 Activity 的布局时,就会用到刚刚为我们创建 PhoneWindow 了,这时其实就是一个把我们的 View 添加到 Window的操作,下面是 Activity 的 setContentView 的代码:




是的调用了 PhoneWindow 的 setContentView ,代码如下:




PhoneWindow 里面会先检查 mContentParent 是否存在,mContentParent 其实就是 id  = com.android.internal.R.id.content 的一个 FrameLayout,也就是我们 setContentView 中 View 的亲老爸,这个一会儿会看到。不存在那就先去搞点事情,于是就调用了 installDecor(),部分如下:



里面如果 DecorView 不存在那就 new 一个DecorView,然后就开始为  调用 generateLayout 为 mContentParent 赋值,里面还会有很多操作,比如主题样式的解析,以 style 中 windowFullscreen 为例:




里面去 get 这个标记,如果 ture 那么说明需要充满,然后就调用 setFlags 设置这个充满标记:




点进去你会发现其实就是为 WindowManager.LayoutParams 设置 Flag 标记,WindowManager.LayoutParams 是在我们向 WindowManager 中发起 addView 请求时带过去的,当然里面还有很多 style 解析操作,他们在一定程度上影响我们的显示。

然后再次省略数以亿计的代码到达如下:



fuck!终于开始创建我们的容器了,in 这个View 一般都是个 LineaLayout, 这个 in 里面包含了我们 id = com.android.internal.R.id.content 的 View ,然后它被加入到了我们的顶级容器 DecorView, 再然后开始 find id = com.android.internal.R.id.content的容器,这样就得到了 contentParent 了,最后再 rerurn 它。之后会根据你的偏好设置检查 ActionBar、TitleBar 之类的隐藏显示。需要注意的是 ActionBar 和 content 是同级的存在。

执行完上面的操作后,其实所有的祖祖辈辈将就都初始化好了,就差生自己了,下面这段代码后就把自己也添加进了家族:




但是问题来了,现在就可以显示了吗?当然是 NO,我们只是把一堆东西通过 PhoneWindow 加入到了DecorView , 然而 DecorView 还没有并没有加入到系统 Window ,真走加入的操作是 ActivityThread的 handleResumeActivity 方法调用完 Activity 的 onResume 后调用的 Acivity 的 makeVisible,如下:




到了这里还有个很奇怪的问题 StatusBar 和 NavigationBar 的操作死哪儿去了?先看个简单的图:




其实是这样的,当系统回调如下方法时就会触发一个检查操作:




这两个方法都是 View 里面的方法, DecorView 复写了它,进入 updateColorViews 我们只找关键的:




又发现了 updateColorViewInt 这个方法,再进去就是对着两个 bar 的操作了:




代码写的很清晰,先判断 show 状态,再判断 bar 的 View 是否为空,为空就随便 new 一个 View 然后根据设置赋予一些属性,如颜色什么的、高度什么的,最后加入到 DecorView 中,不为空就检查一些状态变没,变了就改变参数呗,这就搞定了~~。


经过上面的分析可以知道,DecorView 就是我们能接触到的根布局了,下面是 一个 LinearLayout 和两个 Bar , LinearLayout 里面 会有 ActionBar 之类的东西,和 一个 ID = com.android.internal.R.id.content  的 FrameLayout ,FrameLayout 里面就是我们设置的 View 了。

分析完了,我们想象一下我们既然能拿到 DecorView 这个根布局,那意思不就是我们可以控制里面所以的 View 了?接来下我们来玩玩,我们试试拿到 StatusBar 的View 来搞点事情,想象下我们把它 Gone 掉会是什么效果?

代码如下:



执行前:



执行后:



明显是被我们 Gone 了,也有些变化,但是为什么上面还有字? "My Application" 所在的 Bar 怎么没顶上去?原因很简单。系统提示那一栏,其实是盖在我们的 Window 上面的,也就是说它在最上层,你可以通过改变 style 来控制它的显示与否。置于为什么没顶上去,其实也很简单,经过上面分析 Bar 是在一个 LinearLayout 里面的,其实这个 LinearLayout 是充满的,只是它设置了一个 paddingTop 而已,我们来验证下:




结果:



看样子我的手机是 75 像素,要顶上去只需要去掉 paddingTop 就可以了:




结果:




结果和我们预期的一样,淡然我们只是我们玩玩才这么搞的,开发中完全可以利用 google 提供的一些属性来控制它,讲这方便的文章一搜索一大把就不多说了!

猜你喜欢

转载自blog.csdn.net/mabeijianxi/article/details/73608645