本文源碼基於 Android sdk 26, 爲了邏輯清晰,省略了無關代碼,不排除後期從新加上相關代碼bash
當用戶在activity中 調用setContentView 方法時,將本身的佈局id 設置到activity中。而activity經過內部持有的window對象 來設置。ide
/**
* Set the activity content from a layout resource. The resource will be inflated,
* adding all top-level views to the activity.
* @param layoutResID Resource ID to be inflated.
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
getWindow()方法 返回 內部持有的window對象,其具體實例是PhoneWindow。佈局
mWindow = new PhoneWindow(this, window, activityConfigCallback);
複製代碼
顯然,咱們須要看看phoneWindow中setContentView作了什麼。ui
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
mContentParent.requestApplyInsets();
}
複製代碼
mContentParent 實際上就是一個ViewGroup, setContentView方法實際上就把設置的佈局資源解析成View後,再添加到 mContentParent中,完成整個佈局。so,這個mContentParent從何而來,this
phoneWindow的setContentView 方法中,首先調用installDecor(),初始化DecorView , 和mContentParent。spa
private void installDecor() {
if (mDecor == null) {
//建立 DecorView
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
//解析mContetnParent
mContentParent = generateLayout(mDecor);
}
}
複製代碼
而DecorView 本質上就是一個FrameLayout。code
public class DecorView extends FrameLayout
複製代碼
咱們先看如何初始化DecorView。cdn
protected DecorView generateDecor(int featureId) {
return new DecorView(context, featureId, this, getAttributes());
}
複製代碼
嗯,很簡單。 咱們再看如何初始化 mContentView對象
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
int features = getLocalFeatures();
//根據activity主題中設置的參數 獲取不一樣的系統內置的佈局資源id
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
//將獲取到佈局資源id 添加到mDecor中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//系統內置所有佈局資源中 都會一個id爲ID_ANDROID_CONTENT 的 ViewGroup,獲取到並賦值給contentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
return contentParent;
}
複製代碼
在上面方法中,第一步,會根據當前activity的主題信息 加載不一樣的佈局資源,並添加到DecorView中。 第二步,從佈局資源中獲取 id爲 ID_ANDROID_CONTENT 的view 並賦值給contentParent 返回,這個view即是上文中 mContentParent,在以後,會將用戶本身的佈局 添加到mContentParent中,從而達到界面顯示的目的。blog
/**
*DecorView 直接解析根據主題配置 獲取到的系統佈局id,並添加到本身身上
*/
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
final View root = inflater.inflate(layoutResource, null);
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
複製代碼
因此最終一個Activity界面顯示的佈局應該是這樣的:
因此一個View是如何被添加到屏幕窗口上的呢。