创建一个默认Activity项目,查看setContentView加载view的流程

Android Studio版本 Bumblebee2021.1.1 Patch 3。

项目配置:名称为LayoutApp,编译版本31,适配版本32,最低版本21,包含一个MainActivity,以及它的布局文件activity_main.xml,应用主题为:

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

分析加载过程:

1、MainActivity的setContentView()方法会交给AppCompatDelegateImpl的setContentView()方法来代理设置布局,这个方法里面调ensureSubDecor(),ensureSubDecor方法里面调createSubDecor()方法来加载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();
           ...
        }
    }

createSubDecor方法里面通过mWindow.getDecorView()来调PhoneWindow的installDecor()方法,然后在installDecor()方法里面分别调PhoneWindow的generateDecor方法和generateLayout方法分别初始化DecorView和mContentParent。

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的installDecor()方法说明:generateDecor方法会初始化一个DecorView对象mDecor;接着generateLayout方法会根据主题来选择一个布局layoutResource = R.layout.screen_simple,并通过调用mDecor.onResourcesLoaded方法把layoutResource布局添加到DecorView mDecor里面;然后将layoutResource布局里面id为content的FrameLayout布局实例赋值给PhoneWindow的mContentParent属性。
PhoneWindow的installDecor()方法执行完,此时加载的布局结构为:

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

2、在AppCompatDelegateImpl的createSubDecor()方法加载subDecor布局,这里根据默认主题加载的是R.id.decor_content_parent,根布局:ActionBarOverlayLayout,一个内容容器布局ContentFrameLayout,一个ActionBarContainer模块(里面包含Toolbar和ActionBarContextView)。

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

3、将DecorView中的内容容器FrameLayout里面的子view添加到subDecor的内容容器ContentFrameLayout里面,并清空容器FrameLayout,然后修改DecorView中容器FrameLayout的id为NO_ID(值为-1),修改subDecor中容器ContentFrameLayout的id为content;实际上容器FrameLayout里面没有子view,所以这一步主要是改了容器id。

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

4、在AppCompatDelegateImpl的createSubDecor()方法里面通过mWindow.setContentView(subDecor),调PhoneWindow的setContentView方法,将subDecor布局添加到DecorView中的容器布局FrameLayout里面,并通过Callback的onContentChanged方法通知内容改变。

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、完成AppCompatDelegateImpl的ensureSubDecor()方法对mSubDecor初始化,回到AppCompatDelegateImpl的setContentView()方法里面,通过subDecor的content容器布局加载我们的布局,到此加载布局完成。

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

猜你喜欢

转载自blog.csdn.net/hnjcxy/article/details/124127855