一、布局加载流程
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//分析->布局文件的加载流程,最终调用的是 PhoneWindow.setContentView(int layoutResID)
setContentView(R.layout.activity_main);
}
}
二、分析步骤
1.PhoneWindow.setContentView(int layoutResID)
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// 步骤2
installDecor();
}
...
// 通过 LayoutInflater 把 Activity 的布局文件中的内容添加到系统布局中一个 id 为 content 的 FrameLayout(即 mContentParent )中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
2.PhoneWindow.installDecor()
private void installDecor() {
if (mDecor == null) {
// 步骤3->创建 DecorView
// DecorView 继承 FrameLayout,是窗口的顶级视图,是 Activity 的根布局,包含一个 TitleView 和 ContentView
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 步骤4->初始化 mContentParent
mContentParent = generateLayout(mDecor);
}
}
3.PhoneWindow.generateDecor(int featureId)
protected DecorView generateDecor(int featureId) {
Context context;
// 调用 Activity 的 setContentView() 方法时为 true
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else { // 对应 Application 的 Context
context = getContext();
}
// DecorView 继承了 FrameLayout
return new DecorView(context, featureId, this, getAttributes());
}
4.PhoneWindow.generateLayout(DecorView decor)
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
// Inflate the window decor.
//做一系列的判断,去加载系统的 layout 资源文件
int layoutResource;
int features = getLocalFeatures();
//各种判断加载系统布局
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();
// 步骤5->把系统布局加载到 DecorView 中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
/**
* The ID that the main layout in the XML layout file should have.
* public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
*
* 找一个 id 为 content 的 FrameLayout,Activity 对应的布局内容就是添加在这个 FrameLayout 中
*/
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
mDecor.finishChanging();
// 返回 contentParent
return contentParent;
}
5.PhoneWindow.onResourcesLoaded(LayoutInflater inflater, int layoutResource)
// 将得到的布局文件加载到 DecorView 中
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
// 获取 DecroView 的标题视图 mDecorCaptionView
mDecorCaptionView = createDecorCaptionView(inflater);
// 解析、实例化系统的布局(重点)
final View root = inflater.inflate(layoutResource, null);
// 标题视图 mDecorCaptionView 不为空
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
// 把系统的布局加入到 DecorView 中(宽高都是 MATCH_PARENT)
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else { // 标题视图 mDecorCaptionView 为空
// 把系统的布局加入到 DecorView 中(宽高都是 MATCH_PARENT)
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
}
三、总结
1.Activity.setContentView() 实现了把布局文件中对应的内容加载到 DecroView 中
2.contentParent 是一个 id 为 content 的 FrameLayout,Activity 对应的布局内容就是添加在这个 FrameLayout 中
四、注意
在 Activity 的 onCreate()、onResume() 中,不能直接获取 View 的宽高,因为 View 需要 onMeasure() 之后才能获取到真实宽高,onMeasure() 在 Activity 的 onResume() 之后执行的
版权声明:本文为qq_21586317原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。