Window、PhoneWindow、DecorView和android.R.id.content

findViewById(android.R.id.content),很眼熟;setContentView,天天用。从这两个方向深究下去都会见到Window、PhoneWindow和DecorView。
想知道是什么承载了xml里的布局,想知道查看手机布局最外层总看到的FrameLayout是从哪来的,一切都在这里。

  • Window
    位于 /frameworks/base/core/java/android/view/Window.java。该类是一个抽象类,提供了绘制窗口的一组通用API。可以将之理解为一个载体,各种View在这个载体上显示。

  • PhoneWindow
    位于/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java。该类继承于Window类,是Window类的具体实现,即我们可以通过该类具体去绘制窗口。并且,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。 简而言之,PhoneWindow类是把一个FrameLayout类即DecorView对象进行一定的包装,将它作为应用窗口的根View,并提供一组通用的窗口操作接口。

  • DecorView
    是PhoneWindow类的内部类。该类是一个FrameLayout的子类,并且是PhoneWindow的子类,该类就是对普通的FrameLayout进行功能的扩展,更确切点可以说是修饰(Decor的英文全称是Decoration,即“修饰”的意思),比如说添加TitleBar(标题栏),以及TitleBar上的滚动条等 。最重要的一点是,它是所有应用窗口的根View 。

Window类相当于一幅画(抽象概念,什么画我们未知) ,PhoneWindow为一副齐白石先生的山水画(具体概念,我们知道了是谁的、什么性质的画),DecorView则为该山水画的具体内容(有山、有水、有树,各种界面)。DecorView呈现在PhoneWindow上。

当系统(一般是ActivityManagerService)配置好启动一个Activity的相关参数(包括Activity对象和Window对象信息)后,就会回调Activity的onCreate()方法,在其中我们通过设置setContentView()方法类设置该Activity的显示界面,整个调用链由此铺垫开来。setContentView()的三个构造方法调用流程本质上是一样的,我们就分析setContentView(intresId)方法。

Activity.setContentView(intresId) 该方法在Activity类中,该方法只是简单的回调Window对象,具体为PhoneWindow对象的setContentView()方法实现。

 public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initActionBar();
}

public Window getWindow() { 
    return mWindow;   //Window对象,本质上是一个PhoneWindow对象 
} 

PhoneWindow.setContentView() 该方法在PhoneWindow类中

@Override  
public void setContentView(int layoutResID) {  
    //是否是第一次调用setContentView方法, 如果是第一次调用,则mDecor和mContentParent对象都为空  
    if (mContentParent == null) {  //首先判断mContentParent是否为null,是则调用installDecor()
        installDecor();  
    } else {  //否则移除其内部所有的子Views
        mContentParent.removeAllViews();  
    }
    mLayoutInflater.inflate(layoutResID, mContentParent); //然后通过LayoutInflater.inflate将我们传入的layout放置到mContentParent中
    final Callback cb = getCallback();  
    if (cb != null) {  
        cb.onContentChanged();  
    }  
}

从这里就能看出mContentParent是个ViewGroup且包裹我们整个布局文件;而installDecor()就是去初始化我们这个mContentParent

接下来来看看DecorView,它是PhoneWindow的内部类:

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker 

这时你看到了最外层总看到的FrameLayout是从哪来的了

然后看一下findViewById的代码:

 public View findViewById(int id) {
    return getDecorView().findViewById(id);
}

DecorView算是轴心。

DecorView是一个FrameLayout,然后会根据theme去选择系统中的布局文件,将布局文件通过inflate转化为view,加入到DecorView中;
这些布局文件中都包含一个id为content的FrameLayout,将其引用返回给mContentParent。有了mContentParent,然后把我们写的布局文件通过inflater加入到mContentParent中。

关于android.R.id.content也可以直观的看到。
比如R.layout.xxx可以在frameworks\base\core\res\res\layout里面进行查看,比如R.layout.screen_custom_title.xml:






    
    

    
    
    

上面的title_container是用来放自定义Title的容器,而下面的content就是放置我们设置的布局的容器(android.R.id.content)。

首先初始化mDecor,即DecorView为FrameLayout的子类。就是我们整个窗口的根视图了。
然后,根据theme中的属性值,选择合适的布局,通过infalter.inflater放入到我们的mDecor中。
在这些布局中,一般会包含ActionBar,Title,和一个id为content的FrameLayout。
最后,我们在Activity中设置的布局,会通过infalter.inflater压入到我们的id为android.R.id.content的FrameLayout中去(hierarchy看到两层FrameLayout的原因)

参考:
Android 源码解析 之 setContentView
补充说明Window、PhoneWindow与DecorView

你可能感兴趣的:(Window、PhoneWindow、DecorView和android.R.id.content)