一、布局加載流程
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() 之後執行的