说到View
绘制 必须要提到
Window
WindowManager
WindowManagerService
ViewRootImpl
View
这些之间关系搞不清楚,很难往深理解。以下是一些介绍
1.Window
: 这是一个抽象类,表示一个窗口,具体实现有PhoneWindow
,一般一个Activity
对应一个PhoneWindow
,Window
通俗的说一块显示区域,有层级之分,系统Window
层级最高。
2.WindowManager
: 对于Window的操作通过WindowManager进行IPC操作通知
WindowManagerService
完成,有三个方法addView
updateViewLayout
removeView
。
4.WindowManagerService
:这是一个系统服务,运行在一个独立的进程里,负责管理window的层级
5.ViewRootImpl
:这个大伙都知道,负责绘制View
,联系WindowManager和View。
6.View
:绘制主体,不能单独被绘制,需要依附在Window
上,才能显示。
总结流程就是:WindowManager
使用IPC
通知WindowManagerService
进行添加Window
,WindowManagerService
负责管理Window
层级,当添加完后,通过ViewRootImpl
测量 布局 绘制 View(描述绘制对象),
View
会被绘制到画布上。
最后看下ViewRootImpl
的官方介绍然后了解下是怎么绘制View的
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* 在View和WindowManager之间实现了必须的协议,view的顶层
先看下ViewRootImpl
是什么时候创建的呢?
在前面一篇文章 App的启动流程 得知
收到LAUNCH_ACTIVITY
请求,然后去执行handleLaunchActivity
->handleResumeActivity
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
.....
// 获取activity的window
r.window = r.activity.getWindow();
// 顶层DecorView 是在window里创建的
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 获取WindowManager
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 这里面进行VIew的绘制
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
........
}
这段代码可以知道activity
包含Window
, 实现类PhoneWindow
中可以获取DecorView
,
然后通过WindowManager.addView(decorview,params)
把view
添加到这个Window
作为根节点,调用WindowManagerGlobal的addView
。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
接着会调用ViewRootImpl的setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
requestLayout();
// view measure layout draw之后通过IPC调用
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
然后调用requestLayout()
进行绘制。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
scheduleTraversals
->doTraversal()
->performTraversals()
performTraversals()
代码奇长无比,近千行代码,主体代码已经有人总结了是以下几个方法
1.performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)
2.performLayout(lp, mWidth, mHeight)
3.performDraw()
这三个方法分别对应view的measure
layout
draw
这次不解释源码了 说下主题思路。
measure
测量大小 与之相关的最重要的是WidthMeasureSpec
和 HeightMeasureSpec
这两个参数由宽高和measue模式组成,参数来源于父组件,顶层view的参数来源于getRootMeasureSpec
,默认屏幕宽高,MeasureSpec.EXACTLY组成。
layout
主要在ViewGroup上自定义布局,单个View默认左上角开始。
draw
主要用于单个View Canvas绘制自己需要的内容,ViewGroup一般就绘制背景色和子view等等简单操作。
最后需要调用 WindowSessioin
的addToDisplay
进行IPC通信,最后由WindowManagerService
处理。