android 中view 代表一块图形区域。每一个view 都与一个依赖一个父view。在每一个Activity 中有一个Window 代表一个屏幕,
包含一个View 树和窗口的layout 参数。View tree的root View可以通过getDecorView得到。 在 new 一个activity 时,通常把APK的view和布局通过setContentView(R.layout.activity_main)设置到View树中。
一.在activity中有一个: private Window mWindow; 看一看这个mWindow是怎么来的:在Activity 的attach函数中:
-
mWindow = PolicyManager.makeNewWindow(this);
追踪一下这个函数,看一下调用流程:
./frameworks/base/core/java/com/android/internal/policy/PolicyManager.java:
-
package com.android.internal.policy;
-
-
import android.content.Context;
-
import android.view.FallbackEventHandler;
-
import android.view.LayoutInflater;
-
import android.view.Window;
-
import android.view.WindowManagerPolicy;
-
-
import com.android.internal.policy.IPolicy;
-
-
/**
-
* {@hide}
-
*/
-
-
public final class PolicyManager {
-
private static final String POLICY_IMPL_CLASS_NAME =
-
"com.android.internal.policy.impl.Policy";
-
-
private static final IPolicy sPolicy;
-
-
static {
-
// Pull in the actual implementation of the policy at run-time
-
try {
-
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
-
sPolicy = (IPolicy)policyClass.newInstance();
-
} catch (ClassNotFoundException ex) {
-
throw new RuntimeException(
-
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
-
} catch (InstantiationException ex) {
-
throw new RuntimeException(
-
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
-
} catch (IllegalAccessException ex) {
-
throw new RuntimeException(
-
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
-
}
-
}
-
-
// Cannot instantiate this class
-
private PolicyManager() {}
-
-
// The static methods to spawn new policy-specific objects
-
public static Window makeNewWindow(Context context) {
-
return sPolicy.makeNewWindow(context);
-
}
-
-
public static LayoutInflater makeNewLayoutInflater(Context context) {
-
return sPolicy.makeNewLayoutInflater(context);
-
}
-
-
public static WindowManagerPolicy makeNewWindowManager() {
-
return sPolicy.makeNewWindowManager();
-
}
-
-
public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
-
return sPolicy.makeNewFallbackEventHandler(context);
-
}
-
}
这是一个 final 类,通过反射的方法获取一个com.android.internal.policy.impl.Policy类的静态对象:sPolicy,调用这个类的makeNewWindow:
./frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
-
package com.android.internal.policy.impl;
-
-
import android.content.Context;
-
import android.util.Log;
-
import android.view.FallbackEventHandler;
-
import android.view.LayoutInflater;
-
import android.view.Window;
-
import android.view.WindowManagerPolicy;
-
-
import com.android.internal.policy.IPolicy;
-
import com.android.internal.policy.impl.PhoneLayoutInflater;
-
import com.android.internal.policy.impl.PhoneWindow;
-
import com.android.internal.policy.impl.PhoneWindowManager;
-
-
/**
-
* {@hide}
-
*/
-
-
// Simple implementation of the policy interface that spawns the right
-
// set of objects
-
public class Policy implements IPolicy {
-
private static final String TAG = "PhonePolicy";
-
-
private static final String[] preload_classes = {
-
"com.android.internal.policy.impl.PhoneLayoutInflater",
-
"com.android.internal.policy.impl.PhoneWindow",
-
"com.android.internal.policy.impl.PhoneWindow$1",
-
"com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
-
"com.android.internal.policy.impl.PhoneWindow$DecorView",
-
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
-
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
-
};
-
-
static {
-
// For performance reasons, preload some policy specific classes when
-
// the policy gets loaded.
-
for (String s : preload_classes) {
-
try {
-
Class.forName(s);
-
} catch (ClassNotFoundException ex) {
-
Log.e(TAG, "Could not preload class for phone policy: " + s);
-
}
-
}
-
}
-
-
public Window makeNewWindow(Context context) {
-
return new PhoneWindow(context);
-
}
-
-
public LayoutInflater makeNewLayoutInflater(Context context) {
-
return new PhoneLayoutInflater(context);
-
}
-
-
public WindowManagerPolicy makeNewWindowManager() {
-
return new PhoneWindowManager();
-
}
-
-
public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
-
return new PhoneFallbackEventHandler(context);
-
}
-
}
在这个类中返回了new PhoneWindow(context);
./frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java 现在我们找到了这个Window 对象。Window 的庐山真面目:
-
public class PhoneWindow extends Window implements MenuBuilder.Callback {
继承自 Window。window 的关系搞清楚了。
二.在Activity中通过mWindow.getDecorView() 获取activity view树的布局。又来到了window 中。
-
public final View getDecorView() {
-
if (mDecor == null) {
-
installDecor();
-
}
-
return mDecor;
-
}
DecorView 是PhoneWindow的一个内部类:
-
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
其实就是一个FrameLayout 布局,FrameLayout extend ViewGroup。所有Activity 中的view 都添加到这个布局中。installDecor 函数:
-
private void installDecor() {
-
if (mDecor == null) {
-
mDecor = generateDecor();
-
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-
mDecor.setIsRootNamespace(true);
-
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
-
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
-
}
-
}
-
if (mContentParent == null) {
-
mContentParent = generateLayout(mDecor);
-
......
-
}
-
protected DecorView generateDecor() {
-
return new DecorView(getContext(), -1);
-
}
这就是在PhoneWindow 的mDecorView 。需要注意一下mContentParent 这个变量。下面会分析到。
总结一下在一个Activity 中,有一个PhoneWindwo 对象,这个对象有有一个内部类Decorview ,Decorview extend FrameLayout extend ViewGroup 。这个Activity 的 view 都保存在这里,形成一个View 树。
三.
Activity 的 setContentView:
-
public void setContentView(int layoutResID) {
-
getWindow().setContentView(layoutResID);
-
initActionBar();
-
}
PhoneWindow 的setContentView:
-
public void setContentView(int layoutResID) {
-
if (mContentParent == null) {
-
installDecor();
-
} else {
-
mContentParent.removeAllViews();
-
}
-
mLayoutInflater.inflate(layoutResID, mContentParent);
-
final Callback cb = getCallback();
-
if (cb != null && !isDestroyed()) {
-
cb.onContentChanged();
-
}
-
}
mLayoutInflater.inflate(layoutResID, mContentParent); 在这行代码中,view 被设置进了mContentParent 这个父View中,在 PhoneWindow 中:
private ViewGroup mContentParent
在刚才分析DecorView的时候, 在installDecor中会调用 mContentParent = generateLayout(mDecor), 生成mContentParent;看一下实现:
-
protected ViewGroup generateLayout(DecorView decor) {
-
// Apply data from current theme.
-
......
-
-
// Inflate the window decor.
-
-
int layoutResource;
-
int features = getLocalFeatures();
-
System.out.println("Features: 0x" + Integer.toHexString(features));
-
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
-
if (mIsFloating) {
-
TypedValue res = new TypedValue();
-
getContext().getTheme().resolveAttribute(
-
com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
-
layoutResource = res.resourceId;
-
} else {
-
layoutResource = com.android.internal.R.layout.screen_title_icons;
-
System.out.println("layout.screen.title.icons");
-
}
-
// XXX Remove this once action bar supports these features.
-
removeFeature(FEATURE_ACTION_BAR);
-
// System.out.println("Title Icons!");
-
} 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 = com.android.internal.R.layout.screen_progress;
-
// System.out.println("Progress!");
-
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
-
// Special case for a window with a custom title.
-
// If the window is floating, we need a dialog layout
-
if (mIsFloating) {
-
TypedValue res = new TypedValue();
-
getContext().getTheme().resolveAttribute(
-
com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
-
layoutResource = res.resourceId;
-
} else {
-
layoutResource = com.android.internal.R.layout.screen_custom_title;
-
}
-
// XXX Remove this once action bar supports these features.
-
removeFeature(FEATURE_ACTION_BAR);
-
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
-
// If no other features and not embedded, only need a title.
-
// If the window is floating, we need a dialog layout
-
if (mIsFloating) {
-
TypedValue res = new TypedValue();
-
getContext().getTheme().resolveAttribute(
-
com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
-
layoutResource = res.resourceId;
-
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
-
layoutResource = com.android.internal.R.layout.screen_action_bar;
-
} else {
-
layoutResource = com.android.internal.R.layout.screen_title;
-
}
-
// System.out.println("Title!");
-
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
-
layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
-
} else {
-
// Embedded, so no decoration is needed.
-
layoutResource = com.android.internal.R.layout.screen_simple;
-
// System.out.println("Simple!");
-
}
-
-
mDecor.startChanging();
-
-
View in = mLayoutInflater.inflate(layoutResource, null);
-
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
-
if (contentParent == null) {
-
throw new RuntimeException("Window couldn't find content container view");
-
}
-
-
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
-
ProgressBar progress = getCircularProgressBar(false);
-
if (progress != null) {
-
progress.setIndeterminate(true);
-
}
-
}
-
-
// Remaining setup -- of background and title -- that only applies
-
// to top-level windows.
-
if (getContainer() == null) {
-
Drawable drawable = mBackgroundDrawable;
-
if (mBackgroundResource != 0) {
-
drawable = getContext().getResources().getDrawable(mBackgroundResource);
-
}
-
mDecor.setWindowBackground(drawable);
-
drawable = null;
-
if (mFrameResource != 0) {
-
drawable = getContext().getResources().getDrawable(mFrameResource);
-
}
-
mDecor.setWindowFrame(drawable);
-
-
// System.out.println("Text=" + Integer.toHexString(mTextColor) +
-
// " Sel=" + Integer.toHexString(mTextSelectedColor) +
-
// " Title=" + Integer.toHexString(mTitleColor));
-
-
if (mTitleColor == 0) {
-
mTitleColor = mTextColor;
-
}
-
-
if (mTitle != null) {
-
setTitle(mTitle);
-
}
-
setTitleColor(mTitleColor);
-
}
-
-
mDecor.finishChanging();
-
-
return contentParent;
-
}
一开始获取一些窗口的属性,然后根据这些属性确定 layoutResource 的值,layoutResource 为com.android.internal.R.layout.screen_开头;就是 framework-res.apk里面的资源:src/android$ ls frameworks/base/core/res/res/layout/screen*
frameworks/base/core/res/res/layout/screen_action_bar.xml frameworks/base/core/res/res/layout/screen_simple.xml
frameworks/base/core/res/res/layout/screen_custom_title.xml frameworks/base/core/res/res/layout/screen_title_icons.xml
frameworks/base/core/res/res/layout/screen_progress.xml frameworks/base/core/res/res/layout/screen_title.xml
frameworks/base/core/res/res/layout/screen_simple_overlay_action_mode.xml frameworks/base/core/res/res/layout/screen.xml
然后解析:
-
View in = mLayoutInflater.inflate(layoutResource, null);
-
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
-
if (contentParent == null) {
-
throw new RuntimeException("Window couldn't find content container view");
-
}
contentParent 就是这些xml 文件里的一个ID_ANDROID_CONTENT 的view 。查找 ID_ANDROID_CONTENT定义:
frameworks/base/core/java/android/view/Window.java 124行:
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
打开 screen.xml 文件:
-
<LinearLayout xmlns: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" />
-
<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>
content 是一个FrameLayout的布局,在这几个screen_*.xml文件中 都有一个id为:content的View。也就是说 mDecorView 是一个Activity的根布局,contentParent是mDecorView 一个子布局,提供给程序员的View的根布局。setContentView 的时候以contentParent为父节点。
四.
Activity的默认布局确定了,可以确定一下,修改xml 布局:
-
diff --git a/base/core/res/res/layout/screen_simple.xml b/base/core/res/res/layout/screen_simple.xml
-
index c1914e7..0b7f6e2 100644
-
--- a/base/core/res/res/layout/screen_simple.xml
-
+++ b/base/core/res/res/layout/screen_simple.xml
-
@@ -25,7 +25,8 @@ enabled.
-
android:layout_width="match_parent"
-
android:layout_height="match_parent"
-
android:fitsSystemWindows="true"
-
- android:orientation="vertical">
-
+ android:orientation="vertical"
-
+ android:background="@android:color/holo_orange_dark">
-
<ViewStub android:id="@+id/action_mode_bar_stub"
-
android:inflatedId="@+id/action_mode_bar"
-
android:layout="@layout/action_mode_bar"
-
@@ -37,5 +38,6 @@ enabled.
-
android:layout_height="match_parent"
-
android:foregroundInsidePadding="false"
-
android:foregroundGravity="fill_horizontal|top"
-
- android:foreground="?android:attr/windowContentOverlay" />
-
+ android:foreground="?android:attr/windowContentOverlay"
-
+ android:background="@android:color/holo_green_dark"/>
-
</LinearLayout>
当我们进入桌面的时候,桌面效果如下:
homeshell.png
进入HelloWorld,没有任何变化。分析generateLayout的时候已经看到会根据窗口的属性选择不同的资源文件。桌面和一般的Activity使用了不同的资源配置。
修改screen_action_bar.xml
-
diff --git a/base/core/res/res/layout/screen_action_bar.xml b/base/core/res/res/layout/screen_action_bar.xml
-
index f0b2313..62b34f6 100644
-
--- a/base/core/res/res/layout/screen_action_bar.xml
-
+++ b/base/core/res/res/layout/screen_action_bar.xml
-
@@ -23,33 +23,39 @@ This is an optimized layout for a screen with the Action Bar enabled.
-
android:layout_height="match_parent"
-
android:orientation="vertical"
-
android:fitsSystemWindows="true"
-
- android:splitMotionEvents="false">
-
+ android:splitMotionEvents="false"
-
+ android:background="@android:color/holo_red_dark">
-
<com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
- style="?android:attr/actionBarStyle">
-
+ style="?android:attr/actionBarStyle"
-
+ android:background="@android:color/holo_green_dark">
-
<com.android.internal.widget.ActionBarView
-
android:id="@+id/action_bar"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
- style="?android:attr/actionBarStyle" />
-
+ style="?android:attr/actionBarStyle"
-
+ android:background="@android:color/holo_green_light"/>
-
<com.android.internal.widget.ActionBarContextView
-
android:id="@+id/action_context_bar"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:visibility="gone"
-
- style="?android:attr/actionModeStyle" />
-
+ style="?android:attr/actionModeStyle"
-
+ android:background="@android:color/white"/>
-
</com.android.internal.widget.ActionBarContainer>
-
<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" />
-
+ android:foreground="?android:attr/windowContentOverlay"
-
+ android:background="@android:color/holo_orange_light"/>
-
<com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
style="?android:attr/actionBarSplitStyle"
-
android:visibility="gone"
-
- android:gravity="center"/>
-
+ android:gravity="center"
-
+ android:background="@android:color/holo_blue_light"/>
-
</LinearLayout>
HelloWorld 的效果:
activity.png
从图中可以看到根布局是一个FrameLayout布局,就是mDecorView.DecorView extend FrameLayout。 被选中的区域为FrameLayout,就是mcontentParent,mcontentParent中有一个RelativeLayout,是我们setContentView 的R.layout.activity_main 布局文件。
阅读(3819) | 评论(0) | 转发(0) |