Activity的OnCreate方法中的setContentView方法具体做了哪些事?

这个方法,其实会最终调用PhoneWindow的setContentView方法,下面看看PhoneWindow的源码:(PhoneWindow.java)

    @Override
    public void setContentView(int layoutResID) {
	//当mContentParent为null时,则执行installDecor()
        if (mContentParent == null) {
            installDecor();  //   1
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews(); 
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent); //2
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

从这个方法可以看出,第一次在Activity的onCreate方法中执行setContentView时,mContentParent是为null的,这是会执行installDecor()方法,下面我们看看,installDecor方法的源码:(PhoneWindow.java)

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);// 3
            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); // 4

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
	    //省略部分代码
	    // ...
        }
    }

    protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes()); // 5
    }

从这个方法可以看出,这个方法中,通过generateDecor(-1)方法创建一个mDecor对象,generateDecor方法中,是通过new DecorView(context, featureId, this, getAttributes())的方式来创建DecorView对象的。这里注意一下第三个参数this,这个this,就是代表了当前PhoneWindow,所以,DecorView对象内部是持有PhoneWindow的引用的。看完generateDecor(-1)方法后,我们继续看注释4处,由于第一次加载布局,所以,
mContentParent肯定是为null的,这样就会执行generateLayout(mDecor)方法,下面跟进到这个方法内部,看看具体实现:(PhoneWindow.java)

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();
	//  省略部分代码,这些代码都是根据window的样式来进行一些设置
	// ... 

	//下面这部分代码是加载window的decor
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();

	//下面代码是根据features,来确定相应的布局文件被添加到DecorView中

        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);// 6 

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // 7
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

 
        mDecor.finishChanging();

        return contentParent;
    }

generateLayout这个方法内部,是根据widow的一些样式来进行一些设置,根据features来确定添加到DecorView的布局。确定完要被添加的布局文件的id后, 就通过DecorView的onResourcesLoaded方法来将布局文件的view添加到DecorView中。下面来看看DecorView的onResourcesLoaded(LayoutInflater inflater, int layoutResource)
方法的源码:(DecorView.java)

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        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 {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;   
        initializeElevation();
    }

这个方法内部会先创建一个mDecorCaptionView,在将传入布局文件加载成view,
接着判断,如果mDecorCaptionView不为null,并且mDecorCaptionView的父控件也不为null,则将mDecorCaptionView添加到其父控件中,并且,将传入的布局文件的view添加到
mDecorCaptionView中,如果mDecorCaptionView为null,则直接将传入的布局文件的view,直接添加到DecorView中。说的这里,大家可能会奇怪,DecorView的addView方法是怎么添加view的,其实大家也不必奇怪,因为DecorView其实是继承FrameLayout的,它也是一个ViewGroup,所以,它的添加view的方法其实就是使用的ViewGroup的添加view的方法。下面是DecorView的类的继承关系:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
	//省略代码
}

完成了将传入的布局文件的view添加到DecorView中后,将这个布局文件的viwe赋值给了mContentRoot变量。回到PhoneWindow类的generateLayout方法中,看看注释7的位置,将布局文件的view添加到DecorView中以后,通过findViewById的方法将这个ViewGroup赋值给了contentParent,ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)
最后,返回这个contentParent。到这里,PhoneWindow的installDecor()方法就分析完了,
下面接着看PhoneWindow的setContentView方法的注释2处,mLayoutInflater.inflate(layoutResID, mContentParent); 这里将我们在Activity中的布局文件的id加载到了mContentParent中。这样就完成了将我们自己在Activity的setContetView
方法中设置的布局文件添加到了mContentParent这个ViewGroup中。由于mContentParent是DecorView的子控件,所以,相当于是添加到DecorView中了。

前面分析PhoneWindow的generateLayout方法的注释7处,可能有些人会有疑问,怎么DecorView将一个布局文件作为自己的子view后,怎么就可以通过findViewById(ID_ANDROID_CONTENT)就能找到contentParent呢?这个findViewById方法中传入的id为什么就是ID_ANDROID_CONTENT这个常量呢?我们回到上面的PhoneWindow的ViewGroup generateLayout(DecorView decor) 这个方法内部看看,layoutResource这个变量被赋值的情况,可以看到这个layoutResource可能有如下几种赋值:
R.layout.screen_swipe_dismiss

R.layout.screen_title_icons;

R.layout.screen_progress;

R.layout.screen_custom_title

R.layout.screen_action_bar

R.layout.screen_title

R.layout.screen_simple_overlay_action_mode

R.layout.screen_simple;
这些布局文件位于 frameworks/base/core/res/res/layout/ (参照9.0源码查看)
下面介绍一下常见的系统提供的布局文件

R.layout.screen_title
<!--  
This is an optimized layout for a screen, with the minimum set of features  
enabled.  
-->  
  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:fitsSystemWindows="true">  
    <FrameLayout  
        android:layout_width="match_parent"   
        android:layout_height="?android:attr/windowTitleSize"  
        style="?android:attr/windowTitleBackgroundStyle">  
        <TextView android:id="@android:id/title"   
            style="?android:attr/windowTitleStyle"  
            android:background="@null"  
            android:fadingEdge="horizontal"  
            android:gravity="center_vertical"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent" />  
    </FrameLayout>  
    <FrameLayout android:id="@android:id/content"  
        android:layout_width="match_parent"   
        android:layout_height="0dip"  
        android:layout_weight="1"  
        android:foregroundGravity="fill_horizontal|top"  
        android:foreground="?android:attr/windowContentOverlay" />  
</LinearLayout> 
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> 

其它布局文件,大家可以看看源码,这些布局文件中,都有一个id为 @android:id/content 的FrameLayout,我们在PhoneWindow的generateLayout的注释7处的ID_ANDROID_CONTENT其实就是指的 @android:id/content这个id,所以,能够通过ID_ANDROID_CONTENT来查找其代表的控件FrameLayout。到这里,也可以明白,为什么这个ID_ANDROID_CONTENT的控件取名叫mContentParent了,因为它就是我们传入的布局的父控件,我们传入的
布局都是被添加到它里面的。

到这里,其实只是完成了Decorview的初始化,并将我们自己的布局添加到了Decorview的mContentParent中,但是DecorView还未显示,也不能接收外界的输入,在完成ActivityThread的handleResumeActivity方法的调用后,回调了Activity的onReusme方法后,在接着调用Activity的makeVisible()方法才将DecorView添加到WindowManager,并将DecorView显示。

通过上面整个流程的分析,我们可以总结一下几点:
1.要去除标题栏是,要在setContentView方法前调用requestWindowFeature(Window.FEATURE_NO_TITLE);这是因为,我们DecorView添加布局,是根据feautres来决定添加的布局文件的id的。只有先设置了feature,DecorView才知道去添加哪个布局作为自己的子view。

2.DecorView中,只有一个直接的子view,这个子view要么是DecorCaptionView要么是根据feature拿到的layoutResource,比如R.layout.screen_title 或 R.layout.screen_simple等,上面列出的几种布局中的一种。

系统的状态栏和底部导航栏是不包含在DecorView中,他们是SystemUI的一部分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值