Activity和xml布局如何关联之setContentView方法的内部流程

本文详细探讨了Android中Activity与Window的关系,指出每个Activity都包含一个PhoneWindow实例。通过源码分析,解释了布局加载的流程,从setContentView()开始,经过PhoneWindow和DecorView,最终将布局插入到一个预定义的id为content的FrameLayout中。这一过程揭示了Activity中视图层级的构建及其背后的逻辑。
摘要由CSDN通过智能技术生成

    这是一篇android基础文章,算是复习和巩固一下,追了一下最新Android版本的代码,看看和6年前有什么变化。直接按顺序来了,不想凑字数,反正也没啥人看,就当写笔记了。

有点基础的人都知道,Activity内部先是一个Window,Window内部才是View。开整…

创建一个正常的Activity,我们都知道有3个步骤,1继承Activity;2实现onCreate()方法,内部调用setContentView(xml布局id);3在清单文件中注册。没有感觉到有Window和View的事,不卖关子直接来。

Activity内部有一个mWindow变量,在attach()方法中被赋值,这里发现mWindow是一个PhoneWindow,PhoneWindow是Window抽象类的唯一实现类。也就是说每个Activity都有一个自己的phoneWindow。

public class Activity {

     private Window mWindow;
    
     final void attach(....) {
         mWindow = new PhoneWindow(this, window, activityConfigCallback);
     }
}

attach()方法先于onCreate()方法被调用,so onCreate被调用时候Window对象已经创建好了,接下来就到了Activity的setContentView()方法。

//Activity类中
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
}

调用了window的setContentView(),也就是PhoneWindow的setContentView()。

//PhoneWindow类中
   @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
             installDecor();    // #1 
        } 
             mLayoutInflater.inflate(layoutResID, mContentParent);  // #2
    }

这个方法就已经包括了布局加入到Activity中的全部流程了。
我们先看 #2这行 我们传入的布局id, 最终通过LayoutInflater 加入到了一个叫做mContentParent的ViewGroup对象。

那么mContentParent对象又是啥?在哪里创建的?答案就在 #1

//PhoneWindow类中
private void installDecor() {
      if (mDecor == null) {
            mDecor = generateDecor(-1);  // #3
      }
     if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);  // #4
      }
}

installDecor方法中,完成对mContentParent的赋值(#4),赋值需要一个mDecor变量,发现它的赋值逻辑就在上面代码(#3),而且发现mDecor是一个叫DecorView类型的引用。并且源码中的注释很有料

//PhoneWindow类中

    // This is the top-level view of the window, containing the window decor.
    //这是Window的顶层View
    private DecorView mDecor;

哈,我们找的了Activity中的根view。并且发现它继承自FrameLayout。
哦哦,那也就是说,我们setContentView传入的布局应该会被放入这个DecorView中啊。没错,但接着思考,发现之前(#2) 明明是布局被放入到mContentParent这个ViewGroup中的啊。
我们带着疑问,追一下( #4) mContentParent = generateLayout(mDecor);

//PhoneWindow类中
   protected ViewGroup generateLayout(DecorView decor) {
         //第一步 拿到一个系统提供的xml布局id
         int layoutResource;
         if(xxx){
              layoutResource = R.layout.系统提供的布局id
         } else  if(xxx){
              layoutResource = R.layout.系统提供的布局id
         }else  if(xxx){
              layoutResource = R.layout.系统提供的布局id
         }
          //第二步 
         mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
          //第三步 
         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
         return contentParent;
   }

这个方法里面
第一步先根据条件(设置的主题等,比如有无actionBar? 是否全屏?) 拿到一个系统提供的xml布局id。这些系统布局都有一个共同之处,就是里面都放入一个id为content的FrameLayout。

第二步把 id传给DecorView中的onResourcesLoaded方法

//DecorView类中
 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        final View root = inflater.inflate(layoutResource, null);
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 }

哦哦,是把第一步的系统布局对象,直接加入到DecorView中。并且充满DecorView,放在最底层。

第三步,把系统布局文件中id为content的FrameLayout返回。

我们回过头来再看,( #4) mContentParent = generateLayout(mDecor); 总结它干的事情就是把DecorView中放入了一个系统的布局文件,在把布局文件中的一个FrameLayout 赋值给 mContentParent。

哦哦哦,刚才的疑问解开了,我们自己setContentView传入的布局确实会被放入到DecorView中。但不是直接放,是放入了DecorView中的一个系统提供好的布局中的一个id名为content的FrameLayout。 这也就是方法取名为set"Content"View的原因了。

最后,我们的xml文件就放入到Activity中了。一图胜千言

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值