Kotlin进阶-9-setContentView源析+Window、Activity、DecorView关系

目录

1、介绍

2、关系图

3、源码分析---Window

4、源码分析---PhoneWindow

5、源码分析---DecorView

6、源码分析---setContentView()


1、介绍

在我们Android可见的窗口中,他们除了我们写的xml布局的视图以外,它还有其他层次结构。

1、首先我们的每个窗口都是一个Activity

2、每个Activity中会有一个PhoneWindow(Window的实现类)对象;

3、PhoneWindow会将DecorView设置我们引用窗口的根ViewPhoneWindow来担当ActivityView视图交互的桥梁;

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。 

这是证明PhoneWindowActivityView视图交互的桥梁的证明之一。

/**
 * 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、首先PhoneWindowWindow的唯一实现类,我们在任何使用到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、并且我们获取到DecorViewid=content的视图 mContentParent;

--------------------------------------------------

10、接着我们再回到setContentView中,当我们获取到mContentParent之后,我们需要将我们layout添加到该ViewGroup上,然后根据是否有过场动画,来加载该mContentParent。

 

原创文章 120 获赞 34 访问量 28万+

猜你喜欢

转载自blog.csdn.net/qq_34589749/article/details/106082030