大家在平时的开发中,对于setContentView肯定不陌生,那么对其内部的实现会不会比较好奇呢~~~有幸终于能看到一些PhoneWindow神马的源码,今天就带大家来跑一回源码~~
1、Activity setContentView
首先不用说,进入Activity的setContentView
1 2 3 4 |
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); } |
可以看到里面获取了Window,然后调用了Window的setContentView
2、PhoneWindow setContentView
这里的Window的实现类是PhoneWindow(package com.android.internal.policy.impl;),我们直接看它的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } } |
可以看到,首先判断mContentParent是否为null,是则调用installDecor(),否则移除其内部所有的子Views,然后通过LayoutInflater.inflate将我们传入的layout放置到mContentParent中。
从这里就能看出来mContentParent是个ViewGroup且包裹我们整个布局文件;而installDecor()估计就是去初始化我们这个mContentParent,一会我们会去验证。
接下来,通过getCallBack拿到了一个CallBack对象,其实这个获取到的这个CallBack就是我们Activity自己,你可以去看我们的Activity是实现了CallBack接口的。
这个Callback明显就是一个回调,当PhoneWindow接收到系统分发给它的触摸、IO、菜单等相关的事件时,可以回调相应的Activity进行处理。至于Callback可以回调哪些方法,自己看下这个接口的声明方法即可。当然了这里不是我们的关键,因为我们的setContentView里面只是回调了onContentChanged,而onContentChanged在Activity中是空实现。
好了,接下来去看我们的installDecor()
3、PhoneWindow installDecor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); //... } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); if (mTitleView != null) { //根据FEATURE_NO_TITLE隐藏,或者设置mTitleView的值 //... } else { mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); if (mActionBar != null) { //设置ActionBar标题、图标神马的;根据FEATURE初始化Actionbar的一些显示 //... } } } } |
这里代码比较长,删除了一些初始化Actionbar样式神马的代码。
可以看到这里不仅初始化mContentParent,而且在之前先调用generateDecor();初始化了一个mDecor,mDecor是DecorView对象,为FrameLayout的子类。
在得到mDecor以后设置其焦点的获取方式为,当其子孙都不需要时,自己才获取。
然后通过 generateLayout(mDecor);把mDecor做为参数传入,然后获取到了我们的mContentParent;
接下里就开始通过findViewById进行获取控件了,而这里的findViewById的代码是这样的:
1 2 3 |
public View findViewById(int id) { return getDecorView().findViewById(id); } |
getDecorView返回的就是我们的mDecor。
这里我们猜测下,首先去初始化mDecor,然后通过mDecor初始化了mContentParent,接下来mDecor就可以使用findViewById方法了。那么我觉得,在初始化mDecor的方法
generateDecor()中,一定为我们的mDecor放入了布局或者控件(最简单的就是使用inflate压入了布局文件),而mContentParent可能就是mDecor中的某个子View。
是不是这样呢?
我们一起来先看看generateDecor()方法的实现: