AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

本文详细解析了AppCompatActivity.setContentView()方法中视图加载的整个流程,包括getDelegate()、ensureSubDecor()、createSubDecor()、mWindow.getDecorView()和Window.setContentView()等步骤。通过理解这一过程,可以深入了解Activity如何添加视图,以及setContentView与Window、DecorView和代理类之间的关系。
摘要由CSDN通过智能技术生成

注意:AppCompatActivity.setContentView()与Activity.setContentView()主要的区别,Activity.setContentView直接将视图添加到Window上,AppCompatActivity.setContentView()借助AppCompatActivity的Delegate代理类,将要显示的视图加入到代理层视图,代理层视图在添加到Window上;

目录

1.setContentView()调用流程

1)在AppCompatActivity定义了setContentView

2)getDelegate()

3)ensureSubDecor();

4)createSubDecor()

5)mWindow.getDecorView()

6)Window.setContentView()

2.整个添加在Activity中添加视图的流程如下

3.学习总结


大概可以了解如下内容:

a.AppCompatActivity中在onCreate中调用setContentView(R.layout.main)是做什么的,如何装载视图;

b.PhoneWindow是什么,与Window有什么关系;

c.DecorView如何被创建的,与Window和其他视图是什么关系;

d.在我们调用requestFeature的时候为什么要在setContentView之前?

1.setContentView()调用流程

Activity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);//android.R.id.content
}

1)在AppCompatActivity定义了setContentView

调用添加视图方法;

    //设置要显示视图的布局ID或者View
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

实现了三个重载的setContentView方法,getDelegate()方法负责创建Activity的代理类实例,然后调用setContentView方法添加显示的视图,Activity通过代理模式添加要显示的视图;

2)getDelegate()

负责创建Activity代理AppCompatDelegate类实例;

AppCompatDelegate
    //创建Activity代理类实例
    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

    
    public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
        return create(dialog.getContext(), dialog.getWindow(), callback);
    }

    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else {
            return new AppCompatDelegateImplV14(context, window, callback);
        }
    }
通过代码可以发现根据Android版本不同会实现不同的具体代理类;

代理类的继承关系如下:

具体在代理类AppCompatDelegateImplV9中实现setContentView()方法

 @Override
    public void setContentView(View v) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v);
        mOriginalWindowCallback.onContentChanged();
    }

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }

    @Override
    public void setContentView(View v, ViewGroup.LayoutParams lp) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v, lp);
        mOriginalWindowCallback.onContentChanged();
    }

ensureSubDecor();//负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上(mWindow.setContentView(subDecor);)

ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);//获取mSubDecor下的视图做为父视图
contentParent.removeAllViews();//移除父视图下的所有自视图
contentParent.addView(v);//将Activity下setContentView设置的视图添加到父视图上
mOriginalWindowCallback.onContentChanged();

setContentView调用流程如下:

3)ensureSubDecor();

检测mSubDecor视图是否已经创建,否则负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;

rivate void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

mSubDecorInstalled表示mSubDecor视图是否创建,没创建则调用createSubDecor()方法创建;

4)createSubDecor()

负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;

 private ViewGroup createSubDecor() {
        //获取主题数组
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
        //AppCompatActivity需要设置Theme.AppCompat主题,否则抛出异常
        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        //1.初始化视图显示相关特征
        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
            //样式没有Title
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {           //样式显示ActionBar
            // Don't allow an action bar if there is no title.
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
        }
        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
        a.recycle();

        //确认Window上是否已经安装DecorView,没有则创建DecorView并添加到Window上
        
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;//依据相关参数设置创建subDecor,并添加到Window上

        //2.上面说了主题默认都是NoTitle,所以不会走里面的方法
        if (!mWindowNoTitle) {
            if (mIsFloating) {
                // 如果是弹框Dialog,则加载弹框视图
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);

                // 浮动窗口没有ActionBar,重设置标志
                mHasActionBar = mOverlayActionBar = false;
            } else if (mHasActionBar) {//有ActionBar
                /**
                *这需要一些解释。因为我们不能使用android:theme属性
                *pre-L,我们通过使用
                *ContextThemeWrapper指向actionBarTheme。
                */
                TypedValue outValue = new TypedValue();
                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

                Context themedContext;
                if (outValue.resourceId != 0) {
                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                } else {
                    themedContext = mContext;
                }

                // 通过themedContext加载视图,并设置为内容视图
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent &
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值