android setContentView源码解析

在android开发过程中,看源码是最好的学习方法,也是最痛苦的(学习是快乐的,过程是痛苦的)。真可谓是“成也源码败也源码“。
作为一个android开发的老鸟都知道DecorView这个最顶层view,那么这个DecorView是如何创建的,我们在activity里面创建的用户布局又是如何加载,如何添加到DecorView上 的,小菜鸟就来过一下这个过程。。
在这里我们还需要知道DecorView,Window,PhoneWindow这几个鸟是啥东西,我相信大神的你肯定都是见过的;Window看系统源码是抽象基类,提供了很多设置方法,比如 public View findViewById(@IdRes int id) {},这个方法你肯定见过,就是Window提供的,没见过打我好吧;PhoneWindow是唯一一个继承自Window的类,也就是所有工作都是这哥们完成的,以后所提到的Window也都是PhoneWindow。DecorView又是啥呢,看他的继承关系是一个FrameLayout,现在就豁然开朗了DecorView就是页面的最顶层view,我们所谓的标题栏,内容栏等都在上面。好回到我们的主题

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

     public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

getWindow()肯定就是是 activity里面的 Window,根据上面说的肯定就是PhoneWindow的实例,接着进去PhoneWindow:

 @Override
    public void setContentView(int layoutResID) {

        if (mContentParent == null) {
            installDecor();
        } 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);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

第一次进去的时候mContentParent(id未content的FrameLayout,后面会看到)为空,就是没有初始化DecorView,需要进入installDecor, mLayoutInflater.inflate(layoutResID, mContentParent);加载布局到mContentParent。

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

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
             。。。。。

        }
    }

这里就看到generateDecor里面new DecorView,这个跟布局就这样悄悄的创建了。

 protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
    protected ViewGroup generateLayout(DecorView decor) {

        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
            requestFeature(FEATURE_SWIPE_TO_DISMISS);
        }

        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }

generateLayout 里面代码很多,这里就贴一部分代码,FLAG_FULLSCREEN这个是不是很熟悉,全屏模式。这里面的获取的参数都是用户在style和在activity设置的参数,最后来设置对应的属性,和还在相对应的布局文件,如下面布局xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- Popout bar for action modes -->
    <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"/>
    <RelativeLayout android:id="@android:id/title_container"
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/windowTitleSize">
        <!-- The title background has 9px left padding. -->
        <ImageView android:id="@android:id/left_icon"
            android:visibility="gone"
            android:layout_marginEnd="9dip"
            android:layout_width="16dip"
            android:layout_height="16dip"
            android:scaleType="fitCenter"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true" />
        <ProgressBar android:id="@+id/progress_circular"
            style="?android:attr/progressBarStyleSmallTitle"
            android:visibility="gone"
            android:max="10000"
            android:layout_centerVertical="true"
            android:layout_alignParentEnd="true"
            android:layout_marginStart="6dip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <!-- There are 6dip between this and the circular progress on the right, we
             also make 6dip (with the -3dip margin_left) to the icon on the left or
             the screen left edge if no icon. This also places our left edge 3dip to
             the left of the title text left edge. -->
        <ProgressBar android:id="@+id/progress_horizontal"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="-3dip"
            android:layout_toStartOf="@android:id/progress_circular"
            android:layout_toEndOf="@android:id/left_icon"
            android:layout_centerVertical="true"
            android:visibility="gone"
            android:max="10000" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:layout_toStartOf="@id/progress_circular"
            android:layout_toEndOf="@android:id/left_icon"
            >
            <TextView android:id="@android:id/title"
                style="?android:attr/windowTitleStyle"
                android:layout_width="0dip"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@null"
                android:fadingEdge="horizontal"
                android:scrollHorizontally="true"
                android:gravity="center_vertical"
                android:layout_marginEnd="2dip"
                />
            <!-- 2dip between the icon and the title text, if icon is present. -->
            <ImageView android:id="@android:id/right_icon"
                android:visibility="gone"
                android:layout_width="16dip"
                android:layout_height="16dip"
                android:layout_weight="0"
                android:layout_gravity="center_vertical"
                android:scaleType="fitCenter"
                />
            </LinearLayout>
    </RelativeLayout>
    <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>

或者


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

等等。。。。
`

android:id=”@android:id/content” 这句你肯定很熟悉,我们的内容布局view,activity里面的布局文件最后都加载在这里面,也就是activity.setContentView里面的内容。注意ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)这句,我们generateLayout返回的就是contentParent,始终要记得contentParent是在DecorView内部,DecorView是根view。
setContentView源码解析就告一段落了。。

回顾下
Window是一个抽象类,提供了各种窗口操作的方法,比如设置背景标题ContentView等等
PhoneWindow则是Window的唯一实现类,它里面实现了各种添加背景主题ContentView的方法,内部通过DecorView来添加顶级视图
每一个Activity上面都有一个Window,可以通过getWindow获取
DecorView,顶级视图,继承与FramentLayout,setContentView则是添加在它里面的@id/content里
setContentView里面创建了DecorView,根据Theme,Feature添加了对应的布局文件,最终setContentView会添加在content里面
当setContentView设置显示后会回调Activity的onContentChanged方法

这里写图片描述

图是借的哈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值