setContentView之旅

《setContentView之旅》
《setContentView之旅》

开车了,坐稳吧

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

    原文作者:copyLi
    原文地址: https://www.jianshu.com/p/ab497bd4da20
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞