目录
1、介绍
在我们Android可见的窗口中,他们除了我们写的xml布局的视图以外,它还有其他层次结构。
1、首先我们的每个窗口都是一个Activity;
2、每个Activity中会有一个PhoneWindow(Window的实现类)对象;
3、PhoneWindow会将DecorView设置我们引用窗口的根View,PhoneWindow来担当Activity和View视图交互的桥梁;
4、DecorView:顶层视图,将要显示的具体内容呈现在PhoneWindow上。 DecorView是当前Activity所有View的祖先。
2、关系图
Window、Activity、DecorView的类图如下:
他们之间的关系图如下:
3、源码分析---Window
看下面源码,发现:
1、Window 是一个顶级窗口查看和行为的一个抽象基类。
2、这个类的实例作为一个顶级View添加到Window Manager。
3、它提供了一套标准的UI方法,比如添加背景,标题等等。
4、当你需要用到Window的时候,你应该使用它的唯一实现类PhoneWindow。
--------------------------------------
5、而我们在Activity中使用的 findViewById()和setContentView()方法最后调用的都是该类中的方法,然后再通过该类获取到DecorView,然后从DecorView中获取到我们指定的View。
这是证明PhoneWindow是Activity和View视图交互的桥梁的证明之一。
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
...
@Nullable
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
/**
* Convenience for * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
* to set the screen content from a layout resource. The resource will be * inflated, adding all top-level views to the screen. * * @param layoutResID Resource ID to be inflated.
* @see #setContentView(View, android.view.ViewGroup.LayoutParams)
*/
public abstract void setContentView(@LayoutRes int layoutResID);
...
}
--------------------------------------------------------------------
class Activity{
...
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
}
4、源码分析---PhoneWindow
看下面源码,发现:
1、首先PhoneWindow是Window的唯一实现类,我们在任何使用到Window对象的地方,new的实例其实就是PhoneWindow的实例。
2、我们PhoneWindow类中有DecorView的变量,后面会证明DecorView才是我们视图显示的类,PhoneWindow只是一个工具类,不是一个真正的视图。
3、mContentParent,它的注解说可以是DecorView也可以是DecorView的子View,在后面代码的实现中,我们发现它其实就是我们的书写的XML布局的视图View。
我们可以在我们的Activity中通过下面的方法获取到我们的XML布局的视图View。可以看到我们XML布局中的视图View的ID就是R.id.content。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private final static String TAG = "PhoneWindow";
...
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
private LayoutInflater mLayoutInflater;
private TextView mTitleView;
...
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
protected ViewGroup generateLayout(DecorView decor) {
ViewGroup contentParent = (ViewGroup)findViewById(com.android.internal.R.id.content);
...
return contentParent;
}
}
5、源码分析---DecorView
看下面源码,发现:
1、我们的DecorView(Activity的根View)其实就是一个FrameLayout;
2、DecorView同时保存了上面我们创建的PhoneWindow对象
3、mContentRoot也是我们XML布局的视图View。
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
private PhoneWindow mWindow;
ViewGroup mContentRoot;
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
/** The feature ID of the panel, or -1 if this is the application's DecorView */
private final int mFeatureId;
private final Rect mDrawingBounds = new Rect();
private final Rect mBackgroundPadding = new Rect();
private final Rect mFramePadding = new Rect();
private final Rect mFrameOffsets = new Rect();
....
}
6、源码分析---setContentView()
1、当我们调用Activity中的setContentView()方法的时候会最后调用我们PhoneWindow的setContentView()的方法。
2、在PhoneWindow中,我们可以看到当第一次加载该Activity的时候我们mContentParent=null,需要初始化我们的mContentParent和DecorView,初始化方法为installDecor();
3、当初始化完mContentParent的时候,如果没有过场动画,我们会直接将我们的XML layout 绘制到我们的mContentParent上。
4、在我们的installDecor()方法中,会new 出来我们的DecorView,还会加载放置我们的XML布局的mContentParent的ViewGroup。
5、在我们的generateLayout()方法中,我们会获取到 Theme中设置的参数,然后根据配置来加载我们自己的Activity中的View。
这也是为什么我们设置NO_TITLE 的布局是,需要在setContentView()前面调用requestFeature()的原因。
6、同时这里会根据我们的Theme中的配置获取到我们DecorView的 系统XML布局文件,默认情况获取的是 R.layout.screen_simple;
它的代码如下:你可以看到id=content的地方就是我们自己写的XMl布局layout的View显示的地方了
看到这里:
7、我们已经new 好了 DecorView,并给我们的DecorView加载了系统XML布局视图;
8、我们按照Theme中的配置初始化了我们窗口视图,设置DecorView的背景,还有TitleView的内容。
9、并且我们获取到DecorView中id=content的视图 mContentParent;
--------------------------------------------------
10、接着我们再回到setContentView中,当我们获取到mContentParent之后,我们需要将我们layout添加到该ViewGroup上,然后根据是否有过场动画,来加载该mContentParent。