在Android开发中,通常,为了指定某个Activity的布局,我们都会在其生命周期函数的开始处,指定Activity的UI布局,生命周期如下:
通常,可以在onCreate函数中使用setContentView指定当前Activity的UI,如果我们没有该步骤,那么Activity最终的效果上面第二个图,空空荡荡,什么都没有,只有一个Actionbar.通过setContentView传入一个布局文件,就可以将布局显示到Activity中,但是,这里面的细节如何实现?这里就进行一个分析:
MainActivity#setContentView-->ActionBarActivity#setContentView-->Activity#setContentView:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID); //1
initWindowDecorActionBar(); //初始化Actionbar
}
其中getWindow()返回的是一个抽象类Window,所以要看其实现的代码,需要看子类PhoneWindow中的实现。
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); //1
} 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); //2
}
}
1中的代码功能:因为mContentParent为null,说明当前的Activity还没有父布局,所以为当前的Activity安装一个布局,然后调用2处的代码。
mLayoutInflater.inflate(layoutResID, mContentParent);此处代码说明,setContentView是将我们定义的布局放到以mContentParent为父布局的布局中,那么由此可知,mContentParent必定包括了Actionbar等一些边框,样式之类的。进入installDecor();
PhoneWindow#installDecor: 这里代码非常的长,省去不重要的:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); //产生一个装饰布局。为FrameLayout
mDecor.setIsRootNamespace(true); //设定根命名空间
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); //为mContentParent产生一个布局,布局的基础是上面产生的mDecor布局
…
这里都是进行了一些对我们选择的主题和样式的解析
}
}
PhoneWindow#generateLayout:
protected ViewGroup generateLayout(DecorView decor) {
TypedArray a = getWindowStyle(); //获取window的主题样式属性
//下面就是根据对样式的属性设置Activity的属性
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);
}
. . .
if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
getContext().getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.HONEYCOMB)) {
setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
}
. . .
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
final boolean targetHcNeedsOptions = context.getResources().getBoolean(
R.bool.target_honeycomb_needs_options_menu);
final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
} else {
clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
}
// Inflate the window decor. 实例window的布局
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
//下面会根据Activity的各种特性,选择默认的布局,这里我们使用下面的特性
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
//使得 layoutResource = R.layout.screen_progress; 其中R.layout.screen_progress为系统默认的布局文件
mDecor.startChanging(); //
//实例化刚才设定的布局文件
View in = mLayoutInflater.inflate(layoutResource, null);
//将刚才实例化的布局添加到décor(前面说了,décor是一个FrameLayout)
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//返回当前布局的索引 在后面的布局文件中可以看到
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}//设定背景
mDecor.setWindowBackground(background);
//设定别的特性
}
//将contentParent返回
return contentParent;
}
Android-sdk\platforms\android-21\data\res\layout\screen_progress.xml 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:fitsSystemWindows="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
>
<ProgressBar android:id="@+android:id/progress_circular"
style="?android:attr/progressBarStyleSmallTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dip"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:visibility="gone"
android:max="10000"
/>
<ProgressBar android:id="@+android:id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="2dip"
android:layout_alignParentStart="true"
android:layout_toStartOf="@android:id/progress_circular"
android:layout_centerVertical="true"
android:visibility="gone"
android:max="10000"
/>
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_toStartOf="@android:id/progress_circular"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:scrollHorizontally="true"
/>
</RelativeLayout>
<FrameLayout android:id="@android:id/content" //这个地方要添加我们自己的布局 也是一个Framelayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay"
/>
</LinearLayout>
结论:通过分析,可以知道在onCreate函数中使用setContentView添加的布局文件,最总会通过系统的解析添加到一个window布局中,window布局是系统默认会提供的,也就是说,即便是我们没有给Activity添加任何布局,系统也会有一个默认的布局,只是这个布局的content为空,而我们添加的布局也最后被放在content中。