前面介绍的是View树的创建,但是创建好的View树只有添加到一个Window里才会显示到屏幕上,因为WindowManagerService
服务会为每一个Window创建一块Surface作为画布,View树里所有的View都会绘画到这块Surface上,最后SurfaceFlinger
服务会将所有Window创建的的Surface传递给GPU,最终显示到屏幕上。
通过WindowManager.addView
接口就可以申请创建一个新的Window并添加一个View树,弹出菜单、浮动窗口等自定义的窗口都是通过这个接口显示出来的:
//获得WindowManager服务
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//设置Window参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.setTitle("FloatWindow");
//创建View树
View rootView = getLayoutInflater().inflate(R.layout.float_layout, null);
//创建Window并关联View树
windowManager.addView(rootView, params);
应用中使用的WindowManager
其实是一个WindowManagerImpl
类的实例:
frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
...
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
如上面的代码,WindowManagerImpl.addView()
实现会调用WindowManagerGlobal.addView()
,实际的请求创建Window的逻辑也在这个类里实现:
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public final class WindowManagerGlobal {
private final ArrayList mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =
new ArrayList();
...
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(params);
try {
root.setView(view, params, panelParentView);
} catch (RuntimeException e) {
throw e;
}
}
}
}
WindowManagerGlobal
类里有这三个ArrayList
列表:
mViews
保存着每个Window的根ViewmRoots
保存每个根View对应的ViewRootImpl
mParams
保存的是每个Window的窗口配置参数WindowManager.LayoutParams
WindowManagerGlobal.addView()
的实现里会为每一个Window创建一个ViewRootImpl
实例,以及窗口配置参数WindowManager.LayoutParams
实例,并将传递过来的根View实例保存在上面提到的三个列表里。addView()
实现里最后调用的root.setView()
就是通过ViewRootImpl
将Window与View树关联起来,同时ViewRootImpl
还有控制View树的刷新、显示以及输入事件分发的作用。
Android 系统中的Activity组件也是通过View树与Window显示出来的,只是将创建View树与Window的逻辑封装到了生命周期的处理流程里了。
所有Activity的窗口都是用一个PhoneWindow
类的实例表示的,每个PhoneWindow
都会创建一个内部类DecorView
作为这个窗口中View树的根View。下面的结构图表示的就是Activity
、PhoneWindow
以及DecorView
之间的关系,下面我们通过这个结构图结合相关代码来研究一下Activity主界面的View树的创建过程。
onCreate
里通过调用 setContentView()
设置自己主界面的View树布局,如下面的代码:public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
setContentView()
有多个重载实现,既可以传递布局资源ID也可以直接传递创建好的View。
Activity
里会将setContentView()
设置过来的View传递给PhoneWindow
:
frameworks/base/core/java/android/app/Activity.java
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()返回的就是PhoneWindow的实例。
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
private DecorView mDecor;
private ViewGroup mContentParent;
public void setContentView(int layoutResID) {
...
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
mContentParent
是用作为ContentView
的父View的,setContentView()
设置过来的Layout 资源ID通过LayoutInflater
解析后会被添加到mContentParent
的子View树里
mContentParent
应该是null
的,需要在installDecor()
里初始化所有的装饰View:private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
generateDecor()
的作用是创建出DecorView
的实例,如下面:protected DecorView generateDecor() {
...
return new DecorView(getContext(), -1);
}
generateLayout()
的作用是根据Activity设置的各种feature、Theme创建ContentParent
、ActionBar
等Activity的基础视图框架,如下面:protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
...
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
...
mDecor.finishChanging();
return contentParent;
}
如果Activity
设置了需要ActionBar
,那么就会加载有ActionBar
的布局资源:
if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = R.layout.screen_action_bar;
} else {
layoutResource = R.layout.screen_title;
}
有ActionBar
的界面会使用R.layout.screen_action_bar
布局资源,没有ActionBar
会使用R.layout.screen_title
(5.0引入Material主题后有新引入了一个R.layout.screen_toolbar
):
frameworks/base/core/res/res/layout/screen_action_bar.xml
<com.android.internal.widget.ActionBarOverlayLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/decor_content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
android:theme="?attr/actionBarTheme">
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.internal.widget.ActionBarContainer
android:id="@+id/action_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
android:touchscreenBlocksFocus="true"
android:gravity="top">
......
com.android.internal.widget.ActionBarContainer>
......
com.android.internal.widget.ActionBarOverlayLayout>
contentParent
会取其中ID为R.id.content
的View,应用的Activity里设置的ContentView会被添加到contentParent
的子View里
上面是Activity的View树的创建过程,而Activity的Window需要等到Activity resume的时候才会创建,同样也是调用WindowManager.addView
接口,而根View就是前面创建的DecorView
:
frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
...
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
...
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
}
}