通常我們所看到的Activity和View最直觀的關(guān)系是在onCreate()方法中設(shè)置setContentView(LayoutId)曹体,為activity設(shè)置布局文件,這樣view就在界面上顯示出來了硝烂。這個(gè)方法做的操作如下:
/**
* 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.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
//其實(shí)是調(diào)用了window(PhoneWindow)的setContentView方法
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
activity的setContentView最終調(diào)用的是mWindow的setContentView方法箕别。mWindow的初始化是在activity的attach()方法中做的。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
滞谢。串稀。。
//這里進(jìn)行了初始化狮杨,mWindow的對(duì)象其實(shí)是Window的子類PhoneWindow
mWindow = new PhoneWindow(this);
//此處省略n行代碼
母截。。橄教。清寇。
}
到這里說明了一點(diǎn),activity的setContentView()是調(diào)用了PhoneWindow的setContentView().
接著上PhoneWindow的代碼:
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) {
//1护蝶、初始化DecorView,生成mContentParent
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
// 這個(gè)先不考慮
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//2华烟、加載activity自己的布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//省略n行代碼
。持灰。盔夜。。。
}
phoneWindow中的setContentView()方法一共可以分為兩步比吭。第一步:初始化話DectorView 第二步:加載我們自己設(shè)定的布局绽族。到這里加載activity自己的view就結(jié)束了(ps:至于涉及到FrameWork層的東西這里就不作描述了)。主要來看一下installDecor()的操作衩藤。
private void installDecor() {
if (mDecor == null) {
// 生成DecorView,一個(gè)繼承了FrameLayout的view
//private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
mDecor = generateDecor();
//省略若干代碼
}
if (mContentParent == null) {
//為mContentParent 賦值
mContentParent = generateLayout(mDecor);
}
//省略若干代碼
}
// 生成DecorView
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
這個(gè)方法里面首先生成了一個(gè)DecorView(繼承了FrameLayout),然后調(diào)用了generateLayout(mDecor)為mContentParent 賦值吧慢。mContentParent 是做什么用的呢?還記得當(dāng)年的夏雨荷嗎赏表?哦 不 是記得phoneWindow中的setContentView()中執(zhí)行的mLayoutInflater.inflate(layoutResID, mContentParent)代碼
嗎检诗。沒錯(cuò),這個(gè)mContentParent就是作為了activity自己的布局(也就是我們自己寫的布局)的一個(gè)父布局而存在瓢剿。
接下來就看一下這個(gè)mContentParent到底是誰吧逢慌。
protected ViewGroup generateLayout(DecorView decor) {
//依照慣例 依然是省略n行代碼
。间狂。攻泼。。
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// 根據(jù)不同的features來加載不同的布局
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
鉴象。忙菠。。纺弊。牛欢。。
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//這里 根據(jù)layoutId(上面根據(jù)不同的features 賦值的layoutResource )加載布局
View in = mLayoutInflater.inflate(layoutResource, null);
//把加載的布局放入到decorview中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
//查找id為ID_ANDROID_CONTENT的控件
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
淆游。傍睹。。犹菱。
// 返回id為ID_ANDROID_CONTENT的控件
return contentParent;
}
上面的代碼主要就是完成了加載系統(tǒng)的布局拾稳、把布局放入到dectorview中、查找出com.android.internal.R.id.content的控件已亥。這個(gè)控件就是我們自己寫的布局的父view熊赖。
到這里window、activity虑椎、view之間的關(guān)系就清楚了震鹉。
1、activity的attach方法中執(zhí)行了window的初始化捆姜,window的實(shí)例為PhoneWindow传趾。
2、activity的setContentView(ID)方法最終是調(diào)用的PhoneWindow的setContentView()方法泥技。
3浆兰、PhoneWindow在執(zhí)行setContentView()的過程中生成了一個(gè)frameLayout的子類DecorView.并且根據(jù)feature的類型加載了一個(gè)對(duì)應(yīng)的系統(tǒng)布局,放入了decorview中。系統(tǒng)布局中有一個(gè)id為com.android.internal.R.id.content的framelayout,這個(gè)frameLayout作為一個(gè)父布局加載我們應(yīng)用中自己定義的xml文件簸呈。
也就是說榕订,我們所有看到的頁面其實(shí)都是在window里面的,activity其實(shí)并不直接掌控view而是借助于window展現(xiàn)的view蜕便。下面是時(shí)候放出圖了(這張圖就清楚的展現(xiàn)了activity劫恒、view、window之間的關(guān)系)