标签:
整个过程基本上都在PhoneWindow中完成的。先罗列里面会用到的方法。如下:
//可以看出它mDecor为DecorView对象
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
//findViewById()如下:
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
//获取theme中关于window属性的设置
public final TypedArray getWindowStyle() {
synchronized (this) {
if (mWindowStyle == null) {
mWindowStyle = mContext.obtainStyledAttributes(
com.android.internal.R.styleable.Window);
}
return mWindowStyle;
}
}
public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
//是否有指定的feature。
public boolean hasFeature(int feature) {
return (getFeatures() & (1 << feature)) != 0;
}
//设置feature。上面的getFeatures()只是返回mFeatures
public boolean requestFeature(int featureId) {
final int flag = 1<<featureId;
mFeatures |= flag;
mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
return (mFeatures&flag) != 0;
}
调用Activity#setContentView()最终会调用到PhoneWindow#setContentView。其代码如下:
@Override
public void setContentView(int layoutResID) {
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);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
首先看installDecor的作用:创建一个DecorView对象,并赋值给mDecor。同时为mContentParent进行赋值。最后会调用onContentChanged()回调。
整体逻辑就这么多,关键是要看mContentParent指的是什么——因为它是我们写的布局的父类。
因为在installDecor()中会调用generateLayout(mDecor)生成mContentParent,所以先看generateLayout(mDecor)方法。
其方法看着比较多,但实质就是根据该activity的theme找到对应的布局,inflate该布局并返回id为android:id/content的ViewGroup。
protected ViewGroup generateLayout(DecorView decor) {
TypedArray a = getWindowStyle();// Apply data from current theme.
//一个输出语句,省略
//获取theme中的windowIsFloating属性
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
//根据指定的属性调用requestFeatures()设置一些feature和flags,具体代码略
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);//无标题feature
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
requestFeature(FEATURE_ACTION_BAR);// Don't allow an action bar if there is no title.
}
//略了一部分代码
if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
requestFeature(FEATURE_SWIPE_TO_DISMISS);//侧滑返回。但很可惜api21以后才能用,而且还有bug
}
//略了一部分setFlags与requestFeature代码,它们都是根据a中设置的一些属性值
int layoutResource;
int features = getLocalFeatures();
//根据features值获取layoutResourece的值,它代表一个布局的id。代码略
mDecor.startChanging();
//添加布局,并将布局添加到decor上。
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//获取添加自己View的viewgroup,layoutResource会有好几个,但都会含义一个id为@android:id/content的ViewGroup,例如可以见下面的screen_swipe_dismiss.xml
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//一些关于features进行的设置
mDecor.finishChanging();
return contentParent;
} 具体的解释上注释中。其中ID_ANDROID_CONTRENT定义如下: /**
* 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;
从这个注释中我们还可以看出contentParent的作用。
private void installDecor() {
if (mDecor == null) {
//生成mDecor,并进行一系列的设置
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);//子View不获取焦点时,它才获取焦点
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
//mDecor是一个ViewGroup,这里省略的就是对其中的某些子View进行初始化设置的代码
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.
//一些关于TransitionManager的初始化工作
}
}
主要过程分为两部分:生成mDecor。根据mDecor,通过generateLayout(mDecor)生成mContentParent。然后对mContentParent进行一系列设置。
上面就是整个过程的分析:
先初始化DecorView,然后根据设置的features不同为decorView加载不同的布局。
接着在decorView中find一个id为android:id/content的mContentParent——这即是自己布局的父容器。
最后通过LayoutInflater.inflate将自己的布局添加到mContentParent中。
一个decorview加载的布局:
<com.android.internal.widget.SwipeDismissLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/content"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/> 从中可以看出,它的确有一个id为android:id/content的ViewGroup。标签:
原文地址:http://blog.csdn.net/u010410408/article/details/51331159