深入解析深入解析Android中的中的setContentView加载布局原理加载布局原理
前言前言
对于Android的开发者来说,setContentView大家再熟悉不过了,在我们的Activity中首先就是要用它加载我们的布局,但是应该有一部分人是不知道加载布局的原理,也包括
我,今天就从源码的角度分析setContentView加载布局原理。
准备工作
由于我们使用的Android API部分源码是隐藏的,当我们在AndroidStudio中是不能找到源码的,我们可以去官网下载相应源码去查看,当然在GitHub下载相应版本的API替换我
们sdk下platforms相应api的android.jar。这样我们就可以在AndroidStudio查看到隐藏的api了,可以断点调试帮助我们阅读源码。
本篇文章分析源码是Android7.1(API25)。
Activiy setContentView源码分析源码分析
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
在Activity中setContentView最终调用了getWindow()的setContentView·方法,getWindow()返回的是一个Window类,它表示一个窗口的概念,我们的Activity就是一个
Window,Dialog和Toast也都是通过Window来展示的,这很好理解,它是一个抽象类,具体的实现是PhoneWindow,加载布局的相关逻辑都几乎都是它处理的。
@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) {
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
先判断mContentParent 是否为空,当然第一次启动时mContentParent 时为空的,然后执行installDecor();方法。
mContentParent不为空是通过hasFeature(FEATURE_CONTENT_TRANSITIONS)判断是否有转场动画,当没有的时候就把通过mContentParent.removeAllViews();移除mContentParent节点下
的所有View.再通过inflate将我们的把布局填充到mContentParent,最后就是内容变化的回调。至于mContentParent 是什么东东,先留个悬念,稍后再说。
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}
final int localFeatures = getLocalFeatures();
for (int i = 0; i < FEATURE_MAX; i++) {
if ((localFeatures & (1 << i)) != 0) {
mDecorContentParent.initFeature(i);
}
}
mDecorContentParent.setUiOptions(mUiOptions);
if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
(mIconRes != 0 && !mDecorContentParent.hasIcon())) {
mDecorContentParent.setIcon(mIconRes);
} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&