Create a default Activity project and view the process of loading view with setContentView

Android Studio version Bumblebee2021.1.1 Patch 3.

Project configuration: the name is LayoutApp, the compilation version is 31, the adaptation version is 32, the minimum version is 21, it contains a MainActivity, and its layout file activity_main.xml, and the application theme is:

<style name="Theme.LayoutApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>

Analyze the loading process:

1. The setContentView() method of MainActivity will be handed over to the setContentView() method of AppCompatDelegateImpl to set the layout on behalf of the agent. In this method, ensureSubDecor() is called, and in the ensureSubDecor method, the createSubDecor() method is called to load subDecor.

AppCompatDelegateImpl类简化源码:
    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }
    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();
           ...
        }
    }

In the createSubDecor method, adjust the installDecor() method of PhoneWindow through mWindow.getDecorView(), and then adjust the generateDecor method and generateLayout method of PhoneWindow in the installDecor() method to initialize DecorView and mContentParent respectively.

AppCompatDelegateImpl类简化源码:
    private ViewGroup createSubDecor() {
        //会调installDecor()方法加载PhoneWindow的DecorView mDecor和内容容器
        mWindow.getDecorView();
        //加载subDecor布局
        subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);
        //获取subDecor里面的内容容器布局,并赋值给contentView 
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        //获取PhoneWindow的mDecor里面的内容容器布局,并赋值给windowContentView 
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            //当前windowContentView的子布局是空的,如果不为空就要挪到subDecor的内容容器里去
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            //下面两行代码修改mDecor的内容容器布局的id和subDecor的内容容器布局的id
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);
        }
        //把subDecor加载到mDecor的内容容器里去
        mWindow.setContentView(subDecor);
        return subDecor;
    }

PhoneWindow类简化源码:
    @Override
    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }
    private void installDecor() {
        if (mDecor == null) {
            //加载DecorView
            mDecor = generateDecor(-1);
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            //加载mContentParent布局,并把mContentParent添加到mDecor
            mContentParent = generateLayout(mDecor);
         }
    }

PhoneWindow's installDecor() method description: the generateDecor method will initialize a DecorView object mDecor; then the generateLayout method will select a layout layoutResource = R.layout.screen_simple according to the theme, and add the layoutResource layout to the DecorView mDecor by calling the mDecor.onResourcesLoaded method ; Then assign the FrameLayout layout instance whose id is content in the layoutResource layout to the mContentParent property of PhoneWindow.
After the installDecor() method of PhoneWindow is executed, the layout structure loaded at this time is:

DecorView(继承自FrameLayout,PhoneWindow中的mDecor是整个布局的最顶层)
    LinearLayout
            ViewStub(懒加载一个ActionBarContextView,调用setTitle()方法后它会包含两个竖直排列的title)
            FrameLayout(容器布局,布局id为content,会将这个布局实例赋值给PhoneWindow的mContentParent属性)

2. Load the subDecor layout in the createSubDecor() method of AppCompatDelegateImpl. Here, R.id.decor_content_parent is loaded according to the default theme, root layout: ActionBarOverlayLayout, a content container layout ContentFrameLayout, and an ActionBarContainer module (which contains Toolbar and ActionBarContextView).

subDecor的布局结构:
ActionBarOverlayLayout(继承自ViewGroup)
        ContentFrameLayout(通过include加入布局ContentFrameLayout,id为R.id.action_bar_activity_content)
        ActionBarContainer(继承自FrameLayout)
                Toolbar(继承自ViewGroup)
                ActionBarContextView(继承自AbsActionBarView,AbsActionBarView继承自ViewGroup)

3. Add the subview in the content container FrameLayout in DecorView to the content container ContentFrameLayout in subDecor, and clear the container FrameLayout, then modify the id of the container FrameLayout in DecorView to NO_ID (value -1), and modify the container ContentFrameLayout in subDecor The id is content; in fact, there is no child view in the container FrameLayout, so this step is mainly to change the container id.

mDecor的布局内容容器id更新后:
DecorView
    LinearLayout
            ViewStub
            FrameLayout(id修改为NO_ID,即-1)
            
subDecor的布局内容容器id更新后:
    ActionBarOverlayLayout
            ContentFrameLayout(id修改为android.R.id.content)
            ActionBarContainer
                    Toolbar
                    ActionBarContextView

4. In the createSubDecor() method of AppCompatDelegateImpl, through mWindow.setContentView(subDecor), adjust the setContentView method of PhoneWindow, add the subDecor layout to the container layout FrameLayout in DecorView, and notify the content change through the onContentChanged method of Callback.

DecorView添加subDecor布局后的结构为:
DecorView(PhoneWindow的mDecor)
        LinearLayout
                ViewStub
                FrameLayout(id修改为NO_ID,即-1)
                        ActionBarOverlayLayout (AppCompatDelegateImpl的subDecor)
                                ContentFrameLayout(id为android.R.id.content的布局作为内容容器)
                                ActionBarContainer
                                        Toolbar
                                        ActionBarContextView

5. Complete the ensureSubDecor() method of AppCompatDelegateImpl to initialize mSubDecor, return to the setContentView() method of AppCompatDelegateImpl, load our layout through the content container layout of subDecor, and load the layout here.

最终布局结构为:
DecorView(PhoneWindow的mDecor)
     LinearLayout
         FrameLayout
              ActionBarOverlayLayout (AppCompatDelegateImpl的subDecor)
                    ContentFrameLayout(id为android.R.id.content的布局作为内容容器)
                          我们自己的布局内容activity_main.xml
                    ActionBarContainer
                          Toolbar
                          ActionBarContextView

Guess you like

Origin blog.csdn.net/hnjcxy/article/details/124127855