Activity XML 布局文件的加载

前言

在上篇文章中,知道了Activity的onCreate方法的调用,那么这边文章,来看看setContentView是怎么把layout布局文件加载的。

以MainActivity为例,一般会让它继承Activity、FragmentActivity、AppCompatActivity。其中AppCompatActivity,从Android 21之后引入Material Design的设计方式,为了支持Material Color 、调色板、toolbar等各种新特性,而产生的。

本章以继承Activity和AppCompatActivity为主,进行布局文件的加载。至于FragmentActivity,感兴趣的小伙伴可以自己去研究一下。

文中会出现大量的View和ViewGroup,可能会有小伙伴有点难以区分,为了方便小伙伴们的理解,先补充下View和ViewGroup相关知识。


UI布局层次结构图.jpg

这里稍微解释下View是所有可视化控件的父类,ViewGroup是View类的子类,可以拥有子控件,可以看作是一个存放View的容器。

Activity中setContentView方法

##Activity
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);  //1
        initWindowDecorActionBar();
}

注释1中,getWindow()获取到的是Window的子类PhoneWindow,PhoneWindow对象在Activity的attach()方法中被创建。可以发现布局文件的加载实际是交给PhoneWindow来处理。

##PhoneWindow
public void setContentView(int layoutResID) {
        ...
        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
        }
        ...
}

注释1主要的工作是加载好DecorView。注释2通过布局加载器解析自己写的xml布局文件转换为View树,并把解析出来的View树添加到mContentParent(ViewGroup)布局容器中。

##PhoneWindow
private void installDecor() {
       ...
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
        } else {
            //若不为null, 则直接与当前的 Window 对象绑定
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ... 
        }
}

protected DecorView generateDecor(int featureId) {
        ...
        return new DecorView(context, featureId, this, getAttributes());
}

protected ViewGroup generateLayout(DecorView decor) {
        // 获取AndroidManifest.xml中指定的themes主题
        TypedArray a = getWindowStyle();  //1
        ...
        //设置当前窗口是否有标题
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            //请求指定Activity窗口的风格类型
            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);
        }

        //设置是否添加ActionBar
        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()));
        }

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

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

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

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

        if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
        a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
        if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()
                + ", major: " + mMinWidthMajor.coerceToString());
        if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMajor,
                    mFixedWidthMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMinor,
                    mFixedWidthMinor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMajor,
                    mFixedHeightMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMinor,
                    mFixedHeightMinor);
        }
        if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
            requestFeature(FEATURE_CONTENT_TRANSITIONS);
        }
        if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
        }

        //是否透明
        mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
        ...

        //设置StatusBar颜色
        if (!mForcedStatusBarColor) {
            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
        }
        if (!mForcedNavigationBarColor) {
            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
            mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
                    0x00000000);
        }
        ...
        //根据上面设置的窗口属性Features, 设置相应的修饰布局文件layoutResource
        int layoutResource;  //2
        int features = getLocalFeatures();
        // 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);  //3
        //获取布局中id为content的View的ViewGroup对象
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  //4
        ...
        return contentParent;
}

1.注释1 获取主题样式属性
这里的样式属性是用来描述窗口,而窗口实质上也是View。通常控件的属性是在layout中设置,而窗口的属性是在AndroidManifest.xml中配置的,通过getWindowStyle()获取当前Window在theme中定义的属性,window支持的属性可以参考\frameworks\base\core\res\res\values\attrs.xml 中的<declare-styleable name="Window"> 。获取到属性值之后有与大堆代码是调用setFlags()和requestFeature()给当前window设置属性值,这就是为什么一般在Activity的onCreate()中设置全屏等属性需要在setContentView()之前设置,因为setContentView()之后installDecor()方法已经执行完毕,所以设置是没有效的。

2.注释2 设置布局文件

##screen_simple.xml
<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>

##screen_simple_overlay_action_mode.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <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" />
    <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>

根据上面设置的窗口属性Features, 设置相应的修饰布局文件layoutResource,这些xml文件位于frameworks/base/core/res/res/layout下。无一例外, 无论看几个其中都有一个id为content 的 FrameLayout。用于添加自己的布局。

3.注释3 将注释2中的布局文件解析为View树

##DecorView    *****注释3
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
       ...
        //Decor的标题View是个DecorCaptionView, 用于控制自由格式窗口的特殊屏幕元素环境
        mDecorCaptionView = createDecorCaptionView(inflater);  //3-1
        //通过 LayoutInflate 来创建这个layoutResource 的 View 实例对象 root
        final View root = inflater.inflate(layoutResource, null);  //3-2
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                // 将 mDecorCaptionView 添加进 DecorView 
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            // 将从系统的资源文件获取的 root 加到这个 mDecorCaptionView 中
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            //将 root 加到 DecorView 中
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        //强转成 ViewGroup, 传递给 mContentRoot
        mContentRoot = (ViewGroup) root;  //3-3
        initializeElevation();
}

##DecorView   *****注释3-1
private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
        DecorCaptionView decorCaptionView = null;
        ...
        decorCaptionView = inflateDecorCaptionView(inflater);  //3-1-1
        ...  
        return decorCaptionView;
}

##DecorView    *****注释3-1-1
private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
        final Context context = getContext();
        // We make a copy of the inflater, so it has the right context associated with it.
        inflater = inflater.from(context);
        final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
                null);
        setDecorCaptionShade(context, view);
        return view;
}

##decor_caption.xml    *****注释3-1-1
<com.android.internal.widget.DecorCaptionView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:descendantFocusability="beforeDescendants" >
    <LinearLayout
            android:id="@+id/caption"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:background="@drawable/decor_caption_title"
            android:focusable="false"
            android:descendantFocusability="blocksDescendants" >
        <Button
                android:id="@+id/maximize_window"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:layout_margin="5dp"
                android:padding="4dp"
                android:layout_gravity="center_vertical|end"
                android:contentDescription="@string/maximize_button_text"
                android:background="@drawable/decor_maximize_button_dark" />
        <Button
                android:id="@+id/close_window"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:layout_margin="5dp"
                android:padding="4dp"
                android:layout_gravity="center_vertical|end"
                android:contentDescription="@string/close_button_text"
                android:background="@drawable/decor_close_button_dark" />
    </LinearLayout>
</com.android.internal.widget.DecorCaptionView>

将注释2选定的布局文件inflate为View树,这也是整个窗口的内容(包括title、content等等),将整个窗口内容添加到根DecorView中,将整个窗口内容赋值给mContentRoot(ViewGroup)。

4.注释4 返回ViewGroup对象,用于添加自己的布局

##Window
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

这个ID_ANDROID_CONTENT即DecorView中mContentRoot中的FrameLayout的Id,
也就是setContentView方法布局文件最终加载进去的地方。

方便小伙伴们理解,补充一张图。


视图结构1.png

mContentParent、mContentRoot、及DecorView都可以理解成一个ViewGroup对象。

AppCompatActivity中setContentView方法

##AppCompatActivity
public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
}

this.getDelegate()获取到AppCompatDelegate对象,这里实际上是获取到AppCompatDelegate的子类AppCompatDelegateImpl,跟踪getDelegate()方法,可以看到。而setContentView()方法,是在AppCompatDelegateImpl具体的实现。

##AppCompatDelegateImpl
public void setContentView(int resId) {
        this.ensureSubDecor();   //1
        //获取到id为16908290的容器
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);  
        contentParent.removeAllViews();
        //通过布局加载器解析自己写的xml布局文件转换为View树,并把解析出来的View树添加到contentParent(ViewGroup)布局容器中
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);  
        this.mOriginalWindowCallback.onContentChanged();
}

1.注释1创建DecorView和ViewGroup对象。

##AppCompatDelegateImpl
private void ensureSubDecor() {
        if (!this.mSubDecorInstalled) {
            this.mSubDecor = this.createSubDecor();  //1
            //设置Title
            CharSequence title = this.getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (this.mDecorContentParent != null) {
                    this.mDecorContentParent.setWindowTitle(title);
                } else if (this.peekSupportActionBar() != null) {
                    this.peekSupportActionBar().setWindowTitle(title);
                } else if (this.mTitleView != null) {
                    this.mTitleView.setText(title);
                }
            }
            ...
        }

}

##AppCompatDelegateImpl
private ViewGroup createSubDecor() {
            ...
            this.mWindow.getDecorView(); //1-1
            LayoutInflater inflater = LayoutInflater.from(this.mContext);
            //根据不同的主题属性特性,加载ViewGroup
            ViewGroup subDecor = null;  //1-2
            if (!this.mWindowNoTitle) {    //3根据主题特性,加载不同的布局
                if (this.mIsFloating) {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);
                    this.mHasActionBar = this.mOverlayActionBar = false;
                } else if (this.mHasActionBar) {
                    ...
                    subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null);
                    ...
                }
            } else {
                if (this.mOverlayActionMode) {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);
                } else {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);
                }
                ...
            }

            if (subDecor == null) {
                throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }");
            } else {
                if (this.mDecorContentParent == null) {
                    this.mTitleView = (TextView)subDecor.findViewById(id.title);
                }
                //1-3
                ViewUtils.makeOptionalFitsSystemWindows(subDecor);
                ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);
                ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);
                if (windowContentView != null) {
                    while(windowContentView.getChildCount() > 0) {
                        View child = windowContentView.getChildAt(0);
                        windowContentView.removeViewAt(0);
                        contentView.addView(child);
                    }

                    windowContentView.setId(-1);
                    contentView.setId(16908290);
                    if (windowContentView instanceof FrameLayout) {
                        ((FrameLayout)windowContentView).setForeground((Drawable)null);
                    }
                }

                this.mWindow.setContentView(subDecor);  //1-4
                ...
                return subDecor;
            }
        }
}

1.注释1-1

##PhoneWindow
@Override
public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
}

getDecorView方法中先去判断DecorView有没有创建,没有创建其实都是调用installDecor方法(上面有叙述,这里就不在讲解)。

2.注释1-2

##abc_dialog_title_material.xml
<android.support.v7.widget.FitWindowsLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:fitsSystemWindows="true">

    <TextView
            android:id="@+id/title"
            style="?android:attr/windowTitleStyle"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAlignment="viewStart"
            android:paddingLeft="?attr/dialogPreferredPadding"
            android:paddingRight="?attr/dialogPreferredPadding"
            android:paddingTop="@dimen/abc_dialog_padding_top_material"/>

    <include
            layout="@layout/abc_screen_content_include"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

</android.support.v7.widget.FitWindowsLinearLayout>

##abc_screen_content_include.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>

根据设置的主题属性, 设置相应的布局文件,通过解析器的解析,强转成一个subDecor(ViewGroup)。每一个布局文件中都会有一个include标签,存在一个id为action_bar_activity_content的ContentFrameLayout。

3.注释1-3
判断DecorView是否有存在id=16908290的ViewGroup,16908290这个数值,它是系统R文件生成的,指的是android.R.id.content(上半章节screen_simple.xml中FrameLayout布局),就把所有一个容器(16908290)中所有的子View移动到注释2中的ContentFrameLayout,先把screen_simple.xml中FrameLayoutid改为-1,再把ContentFrameLayout的id修改为16908290,简单点理解就是把DecorView存放自己布局的容器,转移到subDecor(ViewGroup)容器中。

4.注释1-4 subDecor(ViewGroup)添加到DecorView

##PhoneWindow
 @Override
public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);  //1-4-1
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
}

注释1-4-1mContentParent是DecorView中的容器。在上半章节中,mContentParent的作用是存放自己的xml布局,而在这里mContentParent是存放subDecor(ViewGroup),那么subDecor(ViewGroup)巧妙的就变成存放自己的xml布局的容器。

方便小伙伴们理解,补充一张图。


视图结构2.png

备注:文中Android源码版本9.0

作者:Alan
原创博客,请注明转载处....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,302评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,563评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,433评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,628评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,467评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,354评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,777评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,419评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,725评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,768评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,543评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,387评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,794评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,032评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,305评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,741评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,946评论 2 336

推荐阅读更多精彩内容