美文网首页面试题
Android Activity.setContentView(

Android Activity.setContentView(

作者: 折剑游侠 | 来源:发表于2020-01-16 11:09 被阅读0次

setContentView()可以说是大家最熟悉的方法了,Activity中设置xml布局调用。
下面看看源码中具体如何设置根布局的。

首先调用父类AppCompatActivity.setContentView()

    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

AppCompatDelegate是个抽象类
AppCompatDelegate.create()

    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }

实现类是AppCompatDelegateImpl,看看构造方法

    AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback) {
        mContext = context;
        mWindow = window;
        mAppCompatCallback = callback;

        mOriginalWindowCallback = mWindow.getCallback();
        if (mOriginalWindowCallback instanceof AppCompatWindowCallback) {
            throw new IllegalStateException(
                    "AppCompat has already installed itself into the Window");
        }
        mAppCompatWindowCallback = new AppCompatWindowCallback(mOriginalWindowCallback);
        //设置回调
        mWindow.setCallback(mAppCompatWindowCallback);
        //Window背景色
        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
                context, null, sWindowBackgroundStyleable);
        final Drawable winBg = a.getDrawableIfKnown(0);
        if (winBg != null) {
            mWindow.setBackgroundDrawable(winBg);
        }
        a.recycle();
    }

setContentView()最终调用到了代理类AppCompatDelegateImpl.setContentView(),这里是重点

    public void setContentView(int resId) {
        //创建DecorView
        ensureSubDecor();
        //id为content的FrameLayout
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        //移除content上所有view
        contentParent.removeAllViews();
        //加载我们的xml布局到content
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        //回调
        mOriginalWindowCallback.onContentChanged();
    }

AppCompatDelegateImpl.ensureSubDecor()

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            //创建DecorView
            mSubDecor = createSubDecor();

            //ActionBar title
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (mDecorContentParent != null) {
                    mDecorContentParent.setWindowTitle(title);
                } else if (peekSupportActionBar() != null) {
                    peekSupportActionBar().setWindowTitle(title);
                } else if (mTitleView != null) {
                    mTitleView.setText(title);
                }
            }

            applyFixedSizeWindow();

            //创建DecorView回调
            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            //menu
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!mIsDestroyed && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

AppCompatDelegateImpl.createSubDecor()

        ...
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
        //有无title
        if (!mWindowNoTitle) {
            //Activity弹框形式出现
            if (mIsFloating) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);
            }else if (mHasActionBar) {//有ActionBar
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent = (DecorContentParent) subDecor
                        .findViewById(R.id.decor_content_parent);
            }
        } else {
            if (mOverlayActionMode) {//覆盖模式
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }
        }
        //将原有Window的view迁移到我们的ContentView
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            //设置id
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);

            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }
        //DecorView关联到window
        mWindow.setContentView(subDecor);
        return subDecor;

mWindow.setContentView(subDecor)
window在Activity中初始化,实现类是PhoneWindow
Activity.attach()

    final void attach(...){
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
    }

PhoneWindow.setContentView()

    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

createSubDecor()方法根据不同的配置创建不同样式的DecorView,并调用PhoneWindow.setContentView()关联到window。
总结:setContentView()会调用到代理类AppCompatDelegateImpl.setContentView(),这里会先创建真正的顶层布局DecorView,按需创建menu,ActionBar,并添加id为content的FrameLayout子view,然后LayoutInflater将我们设置的xml布局解析成View树,最后添加到content。


DecorView

相关文章

网友评论

    本文标题:Android Activity.setContentView(

    本文链接:https://www.haomeiwen.com/subject/xdqfzctx.html