setContentView()源碼的理解
通俗的解釋
使用場景
在Activity中的onCreate()方法中,我們會使用setContentView()給Activity加載佈局。但實際情況卻不是我們通常使用時理解的那樣。正如setContentView()方法名那樣,是將layout設置給content,而不是設置給Activity。
先放結論
Activity的佈局是有特定的模板,我們通常只是給Content區域填充View
以screen_custom_title.xml模板爲例(查看源碼請按這裏),會得到下圖
簡單梳理一下執行流程:
以findViewById()爲例,來進一步理解
在Activity中,我們使用findViewById()都很熟練,感覺沒什麼特別的。但是在Framework中,想要找到特定的控件對象,會使用
view.findViewById(id);
來獲取相應的控件。實際上,findViewById()是View當中的方法,在Activity中,findViewById()實際上是調用DecorView.findViewById(),只是不給使用者展示。也就是說,DecorView是外層的佈局,也從側面驗證了上圖。
RTFSC解釋
爲了把握整體思路,這裏並沒有詳細地說明每一行代碼
Activity中的相關代碼
public void setContentView(@LayoutRes int layoutResID) {
//會調用PhoneWindow中的代碼
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow中setContentView()相關代碼
public void setContentView(int layoutResID) {
//mContentParent,也就是Content(上圖淺紫色,與Title平級的部分)
if (mContentParent == null) {
//DecorView會在這裏初始化,ViewGroup也在該方法中添加到DecorView中了,同時找打了mContentParent對象
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 {
//將我們的Layout填充到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
installDecor()中相關的代碼,有省略代碼
private void installDecor() {
if (mDecor == null) {
//初始化DecorView
mDecor = generateDecor();
//一些相關的配置
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
//將ViewGroup添加到DecorView中,並將Content找到,返回
mContentParent = generateLayout(mDecor);
mDecor.makeOptionalFitsSystemWindows();
//自帶裝飾的Content
//DecorContentParent名字起的也很生動,帶裝飾的
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
//對decorContentParent的操作
//設置logo,title,icon,options...
} else {
//對Title的一些操作
//...
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
// Only inflate or create a new TransitionManager if the caller hasn't
// already set a custom one.
if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
//一些過渡的操作
//...
}
}
}
generateLayout中相關的代碼,有省略
protected ViewGroup generateLayout(DecorView decor) {
//獲取相關屬性
TypedArray a = getWindowStyle();
if (false) {
//估計是調試用的代碼
//...
}
//各種屬性的設置
//...
//ViewGroup的Layout用的id
int layoutResource;
int features = getLocalFeatures();
//根據設置來確定layoutResource是哪個layout
//...
//做個開始標記
mDecor.startChanging();
//填充ViewGroup
View in = mLayoutInflater.inflate(layoutResource, null);
//將ViewGroup添加到DecorView中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//獲取Content對象
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//一些操作
//...
//做個結束標記
mDecor.finishChanging();
//返回ViewGroup
return contentParent;
}
結語
setContentView()相關的代碼還是很多的,需要抓住重點,這樣纔不會影響整體的思維。
如果看完了還是感覺雲裏霧裏,那就先把圖片記住,剩下的以後再說。
轉載請標明出處:http://blog.csdn.net/qq_26411333/article/details/51541129