Android setContentView流程分析(一)

  对于做Android App的小伙伴来说setContentView这个方法再熟悉不过了,那么有多少小伙伴知道它的调用到底做了多少事情呢?下面就让我们来看看它背后的故事吧?

  setContentView()方法将分为两节来讲:
  第一节:如何获取DecorView和contentParent
  第二节:如何创建R.layout.activity_main.xml布局View到contentParent中

  这节先来讲如何获取DecorView和contentParent
  我们新建一个Activity时,onCreate()方法中就会自动调用setContentView()方法

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

  这里看着是setContentView方法将我们自己写的R.layout.activity_main的布局文件传下去,其实这里这后边还是做了很多事情的。

这里需要分两种情况
一:MainActivity继承于Activity
二:MainAcitivity继承于AppCompatActivity

第一种情况:MainActivity继承于Activity

1.执行setContentView(R.layout.activity_main)方法后会进入到Activity.java的setContentView方法中

 public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到会调用getWindow().setContentView(layoutResID);,这个getWindow()是获取的其实就是phoneWindow
2.执行phoneWindow的setContentView方法

  @Override
    public void setContentView(int layoutResID) {
        // 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) {
        		//创建DecorView,得到mContentParent 
            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 {
        //将我们传入的R.layout.activity_main.xml渲染到mContentParent
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

我们可以看到这里做了两件事情
(1)执行installDecor();创建出DecorView和拿到contentParent

   private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
        //当mDecor == null时调用generateDecor创建一个DecorView
           mDecor = generateDecor(-1);
            ....//省略代码
        } else {
        //当mDecor != null时将这个phoneWindow传个mDecor
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
				....//省略代码
            }
        }
    }

1)执行mDecor = generateDecor(-1);方法

    protected DecorView generateDecor(int featureId) {
   		...//省略代码,这里创建了一个DecorView
        return new DecorView(context, featureId, this, getAttributes());
    }

到这里已经创建除了DecorView
2)执行mContentParent = generateLayout(mDecor);方法

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

        TypedArray a = getWindowStyle();
        ...//省略代码
        } else {
            // Embedded, so no decoration is needed.
            //这里我们就以R.layout.screen_simple为例
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        //将R.layout.screen_simple.xml添加到DecorView中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

			//拿到contentParent  com.android.internal.R.id.content
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        	...//省略代码
        mDecor.finishChanging();

        return contentParent;
    }

  通过设置的属性和创建的Activity的类型会选择一个对应的layoutResource,再通过onResourcesLoaded()方法将选择的layoutResource以addView()的方式添加到创建的DecorView中去,最后执行contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);拿到contentParent,到这一步就会得到下图的窗口布局
在这里插入图片描述

第二种情况:MainAcitivity继承于AppCompatActivity

1.执行setContentView(R.layout.activity_main)方法后会进入到AppCompatActivity.java中的setContentView

 @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

getDelegate()方法中会调用AppCompatDelegate.create(this, this)后new了一个
AppCompatDelegateImpl

  @NonNull
    public static AppCompatDelegate create(@NonNull Activity activity,
            @Nullable AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, callback);
    }

所以这里会走到AppCompatDelegateImpl.java中的setContentView()方法

    @Override
    public void setContentView(int resId) {
    		//创建DecorView,并创建一个subDecor
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

1.执行ensureSubDecor();

  private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
        		//	创建一个subDecor
            mSubDecor = createSubDecor();
					...//省略代码
        }
    }
private ViewGroup createSubDecor() {
   
        // Now let's make sure that the Window has installed its decor by retrieving it
        //确保已经拿到了phoneWinow,其实早在AppCompatAcitivy的onCreate()方法中就拿到了phoneWindow
        ensureWindow();
        //这一步其实就是创建了DeocrView和拿到了contentParent
        mWindow.getDecorView();

		...//省略代码
        if (!mWindowNoTitle) {
            ...//省略代码
            } else if (mHasActionBar) {
            ...//省略代码
        } else {
            if (mOverlayActionMode) {
            ...//省略代码
            } else {
            //上边省略的代码就是通过各种设置的判断最后为subDecor找了一个xml
            //这里就以最简单R.layout.abc_screen_simple.xml为例吧
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }
			...//省略代码
			//contentView是拿到了R.layout.abc_screen_simple.xml中的R.id.action_bar_activity_content
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
			//windowContentView 是拿到了R.layout.screen_simple.xml中的android.R.id.content
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            //这个while循环的是将windowContentView 中已经添加的View全部移除,然后添加到contentView 中
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            //这里将R.layout.screen_simple.xml中content的id设置为NO_ID
            windowContentView.setId(View.NO_ID);
            //这里将R.layout.abc_screen_simple.xml中的R.id.action_bar_activity_content设置为android.R.id.content
            contentView.setId(android.R.id.content)// Now set the Window's content view with the decor
       //将subDecor放入到phonewWinow中
        mWindow.setContentView(subDecor);
			...//省略代码
			//返回subDecor
        return subDecor;
    }

我们以图的方式来讲解上边的各种操作吧
(1) 执行mWindow.getDecorView();之后会构建出下图
在这里插入图片描述
(2)执行 subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);之后,mSubDecor如下图
在这里插入图片描述(3)执行完 windowContentView.setId(View.NO_ID);
![在这里插入图片描述](https://img-blog.csdnimg.cn/983dfcb10f084b80a7e88cc3cdf32f02.png
(4)执行 contentView.setId(android.R.id.content);
在这里插入图片描述(5)执行mWindow.setContentView(subDecor);
在这里插入图片描述
2.执行 ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);通过创建的mSubDecor拿到了android.R.id.content

  至此,已经拿到了DecorView和contentParent,下一步就是将R.layout.activity_main.xml布局的View创建到contentParent,我们将会在下一节中讲解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值