從activity的setContentView()方法從源碼的角度來解析Android繪製UI的流程。android
先來看看View是若是添加到屏幕窗口上的:bash
首先打開activity的setContentView()的方法,源碼以下:app
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}複製代碼
因而可知activity的setContentView()方法中是調用的getWindow()方法的setContentView。getWindow()方法中返回的是Window,Window類是一個抽象類,它只有惟一的一個實現類爲PhoneWindow。全部咱們能夠去PhoneWindow類中查看setContentView方法以下:ide
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}複製代碼
這裏咱們主要關注installDecor()和 mLayoutInflater.inflate(layoutResID, mContentParent)這兩個方法。首先咱們先看看installDecor()方法的實現:佈局
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1); //給mDecor賦值
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
......
}
}
複製代碼
首先在instanllDecor()方法中首先對mDecor判斷是否爲空,mDecor即爲DecorView,DecorView是一個繼承自FrameLayout 的佈局。若是mDecor爲空, 就會調用 generateDecor(),在這個方法中建立了DecorView對象。post
而後又對mContentParent判斷是否爲空,若是爲空則會調用generateLayout(mDecor)方法,在此方法中給mContentParent賦值,咱們再來看看generateLayout(mDecor)代碼的具體實現:ui
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
.......
//根據主題樣式調用requestFeature()和setFlags()等方法。
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
//這裏根據設置的features不一樣來給layoutResource設置不一樣的值。
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
...... 此處省幾百行代碼
} 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();
//將layoutResource解析成view,並將view添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//這裏是獲取到主容器,ID_ANDROID_CONTENT爲主容器的資源id,必定存在,有系統定義 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
return contentParent;
}複製代碼
此方法中重點看mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);調用了DecorView的onResourcesLoaded()方法,下面查看下onResourcesLoaded()方法的實現:this
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
......
//將layoutResource進行解析,建立root對象
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views. 將root這個View對象添加到DecorView中
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}複製代碼
有以上代碼可見DecorView的onResourcesLoaded()方法主要作了兩件事:解析layoutResource爲root對象,並將root添加到DecorView中。spa
總結一下PhoneWindow中的installDecor()方法:經過generateDecor()方法建立了一個DecorView根佈局。 而後調用generateLayout()方法根據不一樣的主題渲染不一樣的佈局並添加到DecorView中。code
phoneWindow在setContentView中調用了installDecor()方法後,而後又調用mLayoutInflater.inflate(layoutResID, mContentParent);這裏layoutResID是咱們activity中設置的佈局id,mContentParent是id爲Android系統定義的@android:id/content 佈局,以下圖所示。最終咱們再activity中定義的佈局就是添加到mContentParent中。
總結:View是如何添加到屏幕上的?
1.系統會建立頂層佈局容器DecorView,DecorView繼承自FrameLayout,是PhoneWindow對象持有的一個實例,它是全部應用的頂層View,在系統內部進行初始化;
2.DecorView初始化完成以後,系統會根據應用程序的主題樣式去加載一個基礎容器,如NoActionBar、DarkActionBar這些主題對應不一樣的基礎容器,可是每一個容器都有android系統內部定義的id爲android.R.content 的FrameLayout的容器。
3.開發者經過setContentView方法設置的layout佈局就是添加到這個FarmeLayout容器中。