Activity布局加载流程
一、布局加载流程
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 中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
2.PhoneWindow.installDecor()
private void installDecor() {
if (mDecor == null) {
//步骤3->创建 DecorView
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//步骤4
mContentParent = generateLayout(mDecor);
}
}
3.PhoneWindow.generateDecor(int featureId)
protected DecorView generateDecor(int featureId) {
Context context;
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 {
context = getContext();
}
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;//DecroView 通常使用的是这个布局文件
}
// 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) {
mDecorCaptionView = createDecorCaptionView(inflater);
//解析、实例化系统的布局
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
//把系统的布局加入到 DecorView 中
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
}
三、总结
- Activity.setContentView() 实现了把布局文件中对应的内容加载到 DecroView 中
- contentParent 是一个 id 为 content 的 FrameLayout,Activity 对应的布局内容就是添加在这个 FrameLayout 中
四、注意
在 Activity 的 onCreate()、onResume() 中,不能直接获取 View 的宽高,因为 View 需要 onMeasure() 之后才能获取到真实宽高,onMeasure() 在 Activity 的 onResume() 之后执行的