专栏名称: 俞其荣
向前跑 迎着冷眼和嘲笑 与 http://yuqirong.me 保持同步更新
目录
相关文章推荐
新周刊  ·  为什么高考英语作文,总要帮李华写信? ·  13 小时前  
FM1031济南交通广播  ·  又一起!确认系淫秽物品,济南家长赶紧回家自查 ·  昨天  
FM1031济南交通广播  ·  又一起!确认系淫秽物品,济南家长赶紧回家自查 ·  昨天  
互联网思维  ·  一个顶尖的本领:吸引贵人 ·  昨天  
51好读  ›  专栏  ›  俞其荣

Window源码解析(一):与DecorView的那些事

俞其荣  · 简书  ·  · 2017-10-28 14:26

正文

请到「今天看啥」查看全文


另外还给 Window 对象设置了窗口管理器,也就是我们经常用到的 WindowManager 。

WindowManager 是外界接触 Window 的入口,也就是说,想要对 Window 进行一些操作需要用过 WindowManager 来完成。

与DecorView的那些事

在开头中说到,Window 是用来负责管理 View 的。

现在 Window 已经创建完毕了,那么到底什么时候与 View 发生了交集了呢?

我们需要深入到 onCreate() 中一个熟悉的方法: setContentView(R.layout.activity_main)

Activity

setContentView(@LayoutRes int layoutResID)

    public void setContentView(@LayoutRes int layoutResID) {
        // 这里 getWindow 得到的正是上面创建的 PhoneWindow 对象
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

发现它调用的是 Window 中的同名方法。

接着到 PhoneWindow 中跟进,查看具体实现的逻辑。

PhoneWindow

setContentView(int layoutResID)

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        
        // mContentParent 是放置窗口内容的父 viewgroup ,可能是 decorView 本身,也有可能是它的子 viewgroup
        // 如果 mContentParent 是空的,那么就说明 decorView 是空的
        if (mContentParent == null) {
            // 创建 decorview
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            // 将 layout 布局加入到 mContentParent 中并去解析 layout xml 文件
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        // 通知 activity 窗口内容已经发生变化了
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

setContentView(int layoutResID) 中,一开始判断了 mContentParent 。mContentParent 其实就是我们设置的 contentView 的父视图。

关于 mContentParent ,在 PhoneWindow 中有注释:

// 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.

意思就是说,当我们不需要 titlebar 的时候,mContentParent 其实就和 DecorView 一样了;有 titlebar 的时候,DecorView 的内容就分为了 titlebar 和 mContentParent 。

所以如果 mContentParent 为空,那么可以说明还没有创建过 DecorView 。

我们总结一下,在 setContentView(int layoutResID) 中主要就是这三件事:

  1. 创建 DecorView 视图对象;
  2. 将自定义的视图 layout_main.xml 进行解析并添加到 mContentParent 中;
  3. 去通知 activity 窗口视图已经改变了,进行相关操作;

我们去 installDecor() 中看看究竟怎么创建 DecorView 的。

installDecor()

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            // 如果 decorview 为空,调用 generateDecor 来创建 decorview
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            // 创建 mContentParent ,也就是 contentView 的父视图
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            ...

            }
        }
    }

installDecor() 中,调用了 generateDecor() 方法来创建 DecorView;







请到「今天看啥」查看全文