本文将涉及到Activity、PhoneWindow、DecorView、LayoutInflater的相关源码分析。
下面从Activity onCreate 中的 setContentView开始
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
进入Activity的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
如果你的Activity继承自AppCompatActivity,则是这样
@Override
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
二者虽然源码看起来不一样,但核心流程是一样的,都是先把DecorView准备好,在把我们的布局给加载进来,所以这里只以Activity为例进行分析。
我们跟进到getWindow().setContentView(layoutResID) 里面,getWindow得到的是Window的子类PhoneWindow,所以具体实现要看PhoneWindow里面的setContentView方法
444行 的mContentParent就是用来承载我们写的布局的view,mContentParent的父布局则是DecorView,这里先上一张图把层次结构先明确一下
图中的frameLayout(R.id.content) 就是这里的mContentParent,整个页面首次展现时mContentParent必然是空的,所以会执行445行 installDecor(),通过方法名就可以知道,这是要把DecorView给准备好。
2683行只是把DecorView给new出来
DecorView继承自frameLayout
DecorView具体长什么样子,还要继续看PhoneWindow的2693行
mContentParent = generateLayout(mDecor); 进入到generateLayout方法
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
//...省略一堆window样式判断、悬浮、透明、actionbar等等
// 下面是关键代码,为DecorView挑选合适的布局layoutResource的最终赋值就决定了DecorView长什么样子
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//选中以后就把这个布局通过LayoutInflater 加载给DecorView
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//这个 ID_ANDROID_CONTENT就是R.id.content,
//这个id在哪个布局文件里定义的呢?就在layoutResource对应的布局里面,我们可以随便打开一个布局看下,比如screen_simple.xml,(上面提到的这些布局在android.jar --- res ---layout 中可以找到以screen_开头)
//通过findViewById 得到的contentParent 就是上面的mContentParent啊, 看到了吧它就是DecorView布局中的一部分。
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
mDecor.finishChanging();
//返回此布局
return contentParent;
}
写于 2022 01 12 睡前,明天接着写。。。



