1.setContentView流程分析

1. setContentView初步分析之继承自Activity
我们创建的MainActivity继承自Activity,在代码中使用setContentView(R.layout.activity_main),查看他在Activity中的源码如下:

public void setContentView(@LayoutRes int layoutResID) {
    
    
    //这里的getWindow方法获取到一个PhoneWindow实例,然后再分析PhoneWindow中的setContentView方法
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

这里的getWindow方法,获取到的其实是一个PhoneWindow的实例,通过Activity.java中的attach()方法中创建的mWindow对象,代码如下:

mWindow = new PhoneWindow(this, window, activityConfigCallback);

然后我们再从PhoneWindow.java中分析setContentView方法,而setContentView方法主要有两个目的(作用):

  1. 创建DecorView/FrameLayout,然后创建mContentParent/ViewGroup(Root对象)
    • installDecor()->generateDecor()->new DecorView()->mDecor->generateLayout(mDecor)->mContentParent
  2. 解析我们的布局文件,利用反射创建View对象
    • mLayoutInflater.inflate(layoutResID, mContentParent);
    1. 创建rootView
      • ->创建布局的rootView(比如一个xml布局文件是以ConstrantLayout作为根布局)
        crateViewFromTag()
        ->onCreateView()/createView()
        ->PhoneLayoutInflater.onCreateView()
        ->PhoneLayoutInflater.createView()
        ->LayoutInflater.createView()
        ->通过反射创建View并返回
        clazz = Class.forName(解析到的控件全路径,classLoader);
        然后通过clazz获取到构造器,最后根据构造器创建出view对象
    2. 创建子View
      • rInflateChildren(parser, temp, attrs, true);
        ->rInflate(parser, parent, parent.getContext(), attrs, finishInflate);迭代遍历while循环
        ->createViewFromTag(parent, name, context, attrs);
        后面的流程同上面创建rootView流程中接createViewFromTag后面的流程

面试题: LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)中参数的作用是什么?

  1. resource 子控件的布局文件
  2. root 父容器
  3. attachToRoot 逻辑判断,是否可以将子控件resource添加到父容器root中去
  • root非空,并且attachToRoottrue,则可以将第一个参数中的布局文件resource添加到父容器root中去
  • root为空,或者attachToRootfalse,则可以将第一个参数中的布局文件resource创建的View返回

流程图如下:
在这里插入图片描述

哪些地方可以创建PhoneWindow?

  1. Activity
  2. Dialog
  3. PopupWindow
  4. Toast

如何布局?使用 mLayoutInflater.inflate(layoutResID, mContentParent);
使用XMLResourceParser解析,传入的参数mContentParent就是root对象,然后调用rInflate方法或者是调用rInflateChildren方法,正常情况下是调用rInflateChildren方法,但是rInflateChildren方法又会去调用rInflate方法,最终返回一个View

2. setContentView初步分析之继承自AppCompatActivity(继承自FragmentActivity)
我们创建的MainActivity继承自AppCompatActivity,在代码中使用setContentView(R.layout.activity_main),查看他在AppCompatActivity.java中的源码如下:

public void setContentView(@LayoutRes int layoutResID) {
    
    
    //delegate是代理的意思,为了实现版本兼容
    //这里的getDelegate返回的是一个AppCompatDelegateImpl类对象,然后在AppCompatDelegateImpl.java类中分析setContentView的方法
    getDelegate().setContentView(layoutResID);
}

这里的getDelegate()方法返回的是AppCompatDelegateImpl对象的实例,然后在AppCompatDelegateImpl.java类中分析setContentView的代码,源码如下:

public void setContentView(int resId) {
    
    
    //通过这个方法创建mSubDecor/ViewGroup;创建DecorView,创建mContentParent;
    //迁移旧的布局文件中的控件到新的布局文件中,替换content为新的布局文件中的ID
    ensureSubDecor();
    //找到新的布局文件中的控件ID
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

首先调用ensureSubDecor()方法创建mSubDecor对象实例(他是一个ViewGroup对象),代码如下:
mSubDecor = createSubDecor();
然后在createSubDecor()方法中,会调用如下两行代码:

ensureWindow();
mWindow.getDecorView();

第二行代码中就会执行创建DecorViewmContentParent对象的操作;
然后创建subDecor局部变量,并把他作为这个方法的返回值返回给mSubDecor对象;
subDecor引入的布局问文件是R.layout.abc_screen_simple,
为了版本兼容,将screen_simple.xmlcontent下面的控件迁移过来,同时移除screen_simple.xmlcontent下面的控件;
然后将sceen_simple.xml文件中id值为content置为空,同时将abc_screen_simple.xml文件中的id更改为content(这个content原本是sceen_simple.xml文件中的id);
达到将旧版本中的布局控件迁移至新版布局文件和contentid兼容的目的;
然后将subDeocr赋值给PhoneWindow,代码如下:
mWindow.setContentView(subDecor);
然后就会执行PhoneWindow.java中的setContentView方法,同Activity中逻辑类似,这里就会执行创建decorView,接着会返回mContentParent对象
流程图如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/tangkunTKTK/article/details/130669396