setContentView之旅

作者: 咸鱼佬 | 来源:发表于2017-05-27 16:17 被阅读51次

    开车了,坐稳吧

    MainActivity

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    

    根据继承不一样的XxxActivity,会跳转到不一样的XxxActivity中,但是都是会跳回到Activity

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

    getWindow()返回的永远都是PhoneWindow

    mWindow = new PhoneWindow(this, window);
    

    直接去PhoneWindow看setContentView方法

    @Override
    public void setContentView(int layoutResID) {
       
        if (mContentParent == null) {
            installDecor();
        } 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);
        }
       
    }
    

    FEATURE_CONTENT_TRANSITIONS 这个属性5.0才有的,就是由activity_main更换到activity_main1的时候展示一种过渡(链接)
    所以现在可以确定mContentParent是我们盛放着我们自定义的xml布局文件
    然后点击installDecor()方法

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            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 = generateLayout(mDecor);
            ......一坨无关代码
        }
    }
    

    generateDecor(-1);会new出一个DecorView并且关联上PhoneWindow,在看看mContentParent的赋值

    protected ViewGroup generateLayout(DecorView decor) {
        
        //第一坨代码....
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //一坨代码...
    
        return contentParent;
    }
    

    layoutResource是一个xml布局文件的id,像我们自定义的activity_main,那么它是怎么来的?是根据略去的第一坨代码根据我们设置的主题,window属性啥的从系统提供的布局文件中选出来的。例如这个:

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

    我们现在先看第一个方法onResourcesLoaded

    //DecorView.java中
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        
        //一坨代码...
        //一声不吭把系统选出来的布局文件加载进去返回View
        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 {
    
            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//加到decorView中
        }
        mContentRoot = (ViewGroup) root;//mContentRoot终于出现了
        initializeElevation();
    }
    

    mDecorCaptionView源码飞机票是7.0出现的。
    不是7.0的时候就会执行把这个布局文件(view root)放到decorView中,而最后就把这个root赋值给mContentRoot,就相当于decorView中包着mContentRoot
    mContentRoot 本来是放在PhoneWindow中的,像mContentParent一样,但是不知啥时候放到DecorView中了
    那么现在回到这

    protected ViewGroup generateLayout(DecorView decor) {
        
        //第一坨代码....
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//刚刚看完我是不是
    
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //一坨代码...
    
        return contentParent;
    }
    

    这里返回的contentParent就是给mContentParent赋值的,

    if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ......一坨无关代码
        }
    

    那么为什么会执行findViewById,这个id为啥是这个?

    /**
     * The ID that the main layout in the XML layout file should have.
     */
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    

    再看看我们上面放出的xml文件是不是有个id为content的ViewGroup
    so,可以知道,我们的布局文件是包在mContentParent,而mContentParent是被包在mContentRoot中的,而mContentRoot是包在decorView中的。

    好了下车吧
    那么你知道requestWindowFeature()为啥在setContentView之前才有效了吗??
    以前csdn的这篇文章,略有不同
    2017/5/27 16:15:46

    相关文章

      网友评论

        本文标题:setContentView之旅

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