概述:我們的app中每個頁面窗口,都是由Window來表示的;Activity實際上也是一種Window類型(應用Window);每個Activity實例中都有一個Window對象,在attach()方法中被初始化。
public class Activity{
private Window mWindow;
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,
Window window, ActivityConfigCallback activityConfigCallback) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
一、Window類為抽象類,首先查看Window類的類注釋
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
}
Window類是定義窗口外觀樣式和行為規范的抽象基類,用于作為頂層的View加到WindowManager中;
PhoneWindow是它唯一的實現類。
二、PhoneWindow
public class PhoneWindow extends Window implements MenuBuilder.Callback{
private DecorView mDecor;
ViewGroup mContentParent;
public PhoneWindow (Context context, Window preservedWindow,
ActivityConfigCallback activityConfigCallback){
this(context;)
mUseDecorContext = true;
if(preservedWindow != null) {
mDecor = (DecorView) preservedWindow.getDecorView();
...
}
-->
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
-->
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
-->
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
//很長一段都是設置一些Window的屬性或者標志;
...
// Inflate the window decor.
//根據設置好的window屬性標志來選擇layout布局, 賦值到layoutResource
int layoutResource;
int features = getLocalFeatures();
// 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 if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();//mChanging = true;修改標志
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重點代碼,請看下一段
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
mDecor.finishChanging();
return contentParent;
}
}
//DecorView是個FrameLayout
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
}
(1)getDecorView()分析:
- getDecorView()調用了installDecor(),
- installDecor()方法中首先調用了generateDecor(), 創建一個DecorView賦值給mDecor,DecorView繼承自FrameLayout;
- 然后調用generateLayout()設置window的一些屬性,根據屬性給layoutResource賦值對應的layoutId,然后執行mDecor.onResourcesLoaded() , 把layoutResource布局繪制成view添加到mDecor中;
- 從layoutResource布局中找到id為android:id/content的ViewGroup,賦值給wmContentParent;
(2)onResourcesLoaded(LayoutInflater inflater, int layoutResource)
//onResourcesLoaded主要是把layoutResource inflate出來,添加到DecorView中
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();
//mBackdropFrameRenderer 用于繪制背景的線程,不為空時先繪制DecorView的背景
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
//window的標題View,顯示與否取決于window的類型和作用場景
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);//創建這個layoutResource 的 View 實例對象 root
if (mDecorCaptionView != null) {
// 先添加 mDecorCaptionView(如果不為null), 再向 mDecorCaptionView 中添加 root
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.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
分析:
這里的重點是把layoutResource的布局繪制成View對象root,然后把root添加為mDecor的子view。
(3)查看一下其中的一些布局(尋找android:id/content)
R.layout.screen_swipe_dismiss
<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"
/>
R.layout.screen_custom_title
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- 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" />
<FrameLayout android:id="@android:id/title_container"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
android:transitionName="android:title"
style="?android:attr/windowTitleBackgroundStyle">
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
R.layout.screen_simple
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<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" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
上面三個布局都有id為android:id/content的控件
總結 PhoneWindow.getDecorView()
一、調用mWindow.getDecorView()方法時,如果mDecor 為空,執行installDecor(),步驟如下:
1.執行generateDecor()方法,創建一個DecorView的實例,賦值到變量mDecor ;
2.執行generateLayout(DecorView decor)方法,把mDecor 傳遞進去;
(2.1)獲取主題設置,配置一些Window的屬性標志;
(2.2)根據Window的屬性標志,給layoutResource選擇對應的layout賦值,這些layout的共同點是都包含一個id為@android:id/content的layout;
(2.3)把2.2步驟的layoutResource繪制出來變成view,添加為DecorView的子view(mContentParent和DecorView中間很可能還有一層LinearLayout,要看布局的選擇)。
(2.4)從layoutResource中找到id為android:id/content的ViewGroup對象,賦值給mContentParent;
二、如果mDecor 不為空(這時Activity已經完成setContentView的方法),返回以DecorView為根布局的整個視圖layout(包括我們setContentView中設置的layout)。
Window視圖層級.png