Android源码解析:深入setContenView().md

带着问题看源码

书接上文,我们了解了Activity的onCreate()方法是如何调用的,接着我们再看一个熟悉的方法setContentView(),这个方法的作用是将我们写好的xml布局文件展示到UI界面上,那么其内部到底是怎么实现的呢?带着这个疑问我们深入Android源码

入口:Activity.setContetnView()方法

当我们在onCreate()方法中调用setContentView(int res)方法,并传入xml布局时,会调用Activity的setContentView方法
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);  
        initWindowDecorActionBar();
    }
复制代码

Activity不做实际操作,而是交给Window去处理

    public Window getWindow() {
        return mWindow;
    }
    //而Window是一个抽象类,我们需要找到他的实现类 
    -->去哪里找他的实现类呢?还记得上一篇文章提到的Activity的attach()方法吗?该方法在onCreate()方法之前调用,其内部就实例化Window对象
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);

        //Window的实现类PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
    }
复制代码

真正的执行者PhoneWindow.setContentView()方法

    private DecorView mDecor;
    ViewGroup mContentParent;

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor(); // 核心方法1.创建根View:DecorView,并为mContentParent赋值
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews(); //清空mContentParent子控件
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent); //往mContentParent中填充布局
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
复制代码

接着查看installDecor()方法

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {   
            mDecor = generateDecor(-1); //1.创建DecorView
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); //2.找到mContentParent

           //...
            } else {
                mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }
           //...
        }
    }

    protected DecorView generateDecor(int featureId) { 
        //直接new出DecorView,DecorView继承自FrameLayout
        //...
        return new DecorView(context, featureId, this, getAttributes());
    }
复制代码

找到mContentParent:generateLayout()

    protected ViewGroup generateLayout(DecorView decor) {
        //拿到应用设置的主题属性
        TypedArray a = getWindowStyle();
       //...

        //调用requestFeature()
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        //...
            
        //根据不同条件调用setFlags()方法
        //...
            
        // 根据条件,确定layoutResource的值,我们以R.layout.screen_simple布局文件为例
        int layoutResource;
        int features = getLocalFeatures();
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            //....
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        //将布局文件填充到DecorView中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        //ID_ANDROID_CONTENT 的值为:com.android.internal.R.id.content
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        //...
        return contentParent;
    }
        
DecorView.填充内容布局
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mStackId = getStackId();

        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // 调用了addView()将跟布局添加到DecorView中
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }
复制代码

布局文件R.layout.screen_simple

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
复制代码

经过调用installDecor()方法,mContentParent就是id为content的一个FragmeLayotu控件,最后调用inflate方法将我们设置的布局文件填充到mContentParent中. 来一张关系表

UML调用时序图

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值