setContentView究竟干了什么?(API21)

setContentView方法解析

先贴代码

    /**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

这是Activity中的setContentView方法的实体,我们可以看到干了两件事情:

  1. 调用了Window的setContentView方法
  2. 实处化ActionBar
    我们主要关注Window的setContentView方法。
    从Activity的attach方法可以知道,mWindow的实体是PhoneWindow:
mWindow = PolicyManager.makeNewWindow(this);

通过一系列的查找,我们可以知道,mWindow的实体其实是PhoneWindow。
该类在framework源码的路径为base/policy/src/com/android/internal/policy/impl/PhoneWindow.java。

    @Override
    public void setContentView(int layoutResID) {

        if (mContentParent == null){
            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 {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

以上为PhoneWindow的setContentView源码,我们关注两个点:

  1. 当mContentParent为null时,初始化decor
  2. 调用LayoutInflater的inflate方法将我们的布局加入到mContentParent中
    那么mContentParent是什么呢?我们继续看installDecor方法:
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            //此处省略一些次要代码
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            //此处省略一些次要代码
        }
    }

非常清晰,两步走:
1. 初始化mDecor
2. 初始化mContentParent

初始化Decor的代码很简单:

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

DecorView本质上是一个FrameLayout在此不做太多解析。我们主要看一下generateLayout方法:

    protected ViewGroup generateLayout(DecorView decor) {
        //此处省略很长很长解析Window的feature以及flag的代码
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        //省略一些次要代码。。。

        mDecor.finishChanging();

        return contentParent;
    }

generateLayout方法从开始到mDecor.startChanging()的部分其实只做了一件事情,就是根据mLocalFeatures变量寻找到一个布局的模板。这些模板我们可以在frameworks/base/core/res/res/layout/目录下面找到。这些布局模板有一个共同的特点,就是它们都包含一个id为com.android.internal.R.id.content的FrameLayout,而这个Layout就是放置我们自定义布局内容的父布局,因此我们可以看到代码中使用了一个变量名为contentParent的局部变量来存储该布局,并在最后赋给了mContentParent。

同时我们还可以看到一个名为mContentRoot的变量,改变量存储着那些布局模板的根布局。通常情况下mContentRoot和mContentParent并不像等,也有例外的情况,如:调用requestWindowFeature(Window.FEATURE_SWIPE_TO_DISMISS)使用了screen_swipe_dismiss.xml的布局模板。在不相等的情况下,mContentRoot会包含一些其他的界面元素,如ActionBar、ToolBar等。

对setContentView方法的解析到这里就分析的差不多了。我们总结一下它干了些什么:
1. 初始化DecorView(对于一个window来说最顶级的view)
2. 根据Window的属性选择合适的布局模板add到DecorView中
3. 将我们的布局add到一个id为com.android.internal.R.id.content的ViewGroup中
以上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值