Activity(三)--setContentView()

586 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

我们接着看Activity,这篇来分析一下setContentView()

一、setContentView()的作用

我们会在onCreate()方法中做初始化工作,首先就要调用setContentView()加载布局,那来一起学习一下setContentView()的原理

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

一层层点进源码,我们会发现,最终调用的是Activity的setContentView(),但是里面就两行代码,我们还需要接着看

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

调用了getWindow()的setContentView()方法

private Window mWindow;

public Window getWindow() {
    return mWindow;
}

所以getWindow()是Window,我们找到mWindow的初始化

final void attach(...) {
  mWindow = new PhoneWindow();
}

原来mWindow就是PhoneWindow,所以我们找一下PhoneWindow的setContentView(其实Window只有PhoneWindow这一个子类,并且Windows和Activity是一一对应的关系)

@Override
public void setContentView(int layoutResID) {
  if (mContentParent == null) {
    installDecor();
  }
  ...
  mLayoutInflater.inflate(layoutResID, mContentParent);
  ...
}

在PhoneWindow的setContentView()中有两个方法

  • installDecor()
  • mLayoutInflater.inflate()

我们首先看看installDecor()具体逻辑

private void installDecor() {
  if (mDecor == null) {
    mDecor = generateDecor();
  }
  if (mContentParent == null) {
    mContentParent = generateLayout(mDecor);
  }
}

首先调用generateDecor()方法创建了DecorView实例,然后通过generateLayout()返回contentParent,最终赋值给mContentParent

protected ViewGroup generateLayout(DecorView decor){
  ...
  View in = mLayoutInflater.inflate(layoutResource, null);
  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  ...
  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  ...
  return contentParent;
}

我们在这里可以得出installDecor()是将系统的一个布局文件转化成View,并将这个view添加到DecorView中,并且PhoneWindow持有DecorView,每个Activity都对应一个PhoneWidow。mContentParent是放置窗体的容器,也就是我们所加入的View视图树

我们总结一下setContentView()的调用顺序

  • Activity.setCotentView
  • PhoneWindows.setContentView
  • PhoneWindows.installDecor
  • ActivityThread.handleResumeActivity

虽然setContentView()完成了layout的布局,但是其实还未显示,真正添加进来是在ActivityThread的handleResumeActivity()

ActivityClientRecord r = performResumeActivity(token, clearHide);
...
if (r.window == null && !a.mFinished && willBeVisible) {
  ...
  View decor = r.window.getDecorView();
  decor.setVisibility(View.INVISIBLE);
  ViewManager wm = a.getWindowManager();
  ...
  if (a.mVisibleFromClient) {
    a.mWindowAdded = true;
    wm.addView(decor, l);
  }
} 

二、setContentView()用法

  • setContentView()可以设置Activity界面
  • 在Activity中动态切换显示的View
  • 在onContentChanged中进行Activity View成员变量的初始化操作