面试题之View的绘制流程
前言
view
的绘制方法:
-
measure
:判断是否需要重新计算View
的大小 -
layout
:判断是否需要重新计算View
的位置 -
draw
:判断是否需要重新绘制View
源码分析
这次分析源码我们就从Activity
的启动流程那里分析,当Activity
初始化Window
和将布局添加到PhoneWindow
的内部类DecorView
类之后,ActivityThread
类会调用handleResumeActivity
方法将顶层视图DecorView
添加到PhoneWindow
窗口,来看看handlerResumeActivity
方法的实现:
public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//这里调用了WindowManager的addView()方法,而我们知道WindowManager的具体实现类是WindowManagerImpl,我们到WindowManagerImpl中查看addView()的实现
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
...
}
}
public final class WindowManagerImpl implements WindowManager {
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//这里调用了WindowManagerGlobal类的addView()方法
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//这里调用了ViewRootImpl的setView()方法
root.setView(view, wparams, panelParentView);
}
}
}
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
//这里终于到了我们今天要开始分析的真正的主角
requestLayout();
...
}
}
从上面代码中我们可以得知在ActivityThread
类的handleResumeActivity()
方法中,我们将DecorView
视图添加到了WindowManager
中,而在WindowManagerGlobal
中又将ViewRootImpl
和DecorView
进行了绑定,并且在ViewRootImpl
中调用了requestLayout()
代码,接下来我们来分析ViewRootImpl
中requestLayout()
的方法。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//发送绘制View的message
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//我们主要关注这里,发送了一个mTraversalRunnable,具体的实现就是在这个Runnable接口实现类中的run方法进行的
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
...
if (!mStopped || mReportNextDraw) {
...
////获得view宽高的测量约束信息,
//MeasureSpec:UNSPECIFIED 一般不会对子View做任何限制,用于ListView
//MeasureSpec:EXACTLY 父视图对子视图指定了一个确定尺寸,比如我们子View的MATCH_PARENT,或者具体的值
//MeasureSpec:AT_MOST 父视图对子视图指定了一个最大尺寸,比如我们子View的WRAP_CONTENT
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
//执行测量操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
if (didLayout) {
//执行布局操作
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
}
if (!cancelDraw && !newSurface) {
...
//执行绘制操作
performDraw();
}
...
}
}
从以上代码我们可以得出最终的view
的绘制操作是在ViewRootImpl.performTraversals()
方法中进行的performMeasure()
测量,performLayout()
布局摆放,performDraw()
绘制。那接下来我们先去看看测量操作。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//这里调用了View的measure()方法
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
if (forceLayout || needsLayout) {
...
if (cacheIndex < 0 || sIgnoreMeasureCache) {
//回调到我们自己需要重写的onMeasure()方法
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
...
}
...
}
}
//这里因为整个Activity的测量是由DecorView开始的,而DecorView是继承自FrameLayout,那么我们来看看FrameLayout类中的onMeasure方法的实现
public class FrameLayout extends ViewGroup {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
////测量FrameLayout下每个子视图View的宽和高,measureChildWithMargins是在ViewGroup下面的
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
}
}
}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//这里调用子View的measure()方法进行测量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
ViewRootImpl
的performMeasure()
最终执行到了View
的measure()
方法,在该方法中调用了我们经常需要重写的onMeasure()
方法,因为整个Activity
的测量是由DecorView
开始的,而DecorView
是继承自FrameLayout
,所以在FrameLayout
中onMeasure()
方法中又遍历了其子View
,在其ViewGroup
的measureChildWithMargins()
调用了子View
的measure()
方法。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
try {
//在这里调用了View的layout()方法
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
}
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//这里通过获取父布局的摆放来判断是否需要重新摆放View的位置
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
//调用onLayout()方法进行布局摆放
onLayout(changed, l, t, r, b);
...
}
...
}
//这里我们可以看到View中的onLayout()方法是个空实现,因为View没有子类,所以不需要实现摆放,我们可以去ViewGroup看看具体实现
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
//我们这里可以看到ViewGroup这里的方法为抽象方法,需要我们自定义的ViewGruop去实现,我们可以看看系统的FrameLayout是来如何实现的
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
}
public class FrameLayout extends ViewGroup {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//这里调用了摆放子布局的方法
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
//获取子View的个数
final int count = getChildCount();
//获取各种Padding
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
//对子View进行循环
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//获取子View的宽高
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
//获取子view的边距
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
//这里就对子View进行摆放
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
//同时这里又调用了子View的layout()方法
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
}
ViewRootImpl
的performLayout()
方法中调用了View
的layout()
方法,在该方法中调用了自身的onLayout()
,而这个方法又是空实现,因为其自身没有子view
,所以我们找到了继承自ViewGroup
的FrameLayout
的onLayout()
方法,layoutChildren()
中进行了子view
的child.layout()
方法进行子布局的摆放。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performDraw() {
...
try {
//这里调用了本身的draw()方法
boolean canUseAsync = draw(fullRedrawNeeded);
...
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...
}
private boolean draw(boolean fullRedrawNeeded) {
//drawSoftware()是关键方法
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
return useAsyncReport;
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Draw with software renderer.
final Canvas canvas;
try {
....
canvas = mSurface.lockCanvas(dirty);
....
//这里真正的调用了View的draw()方法
mView.draw(canvas);
...
}
}
return true;
}
}
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public void draw(Canvas canvas) {
...
// Step 1, draw the background, if needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 这里调用onDraw方法
if (!dirtyOpaque) onDraw(canvas);
// 这里调用绘制子view的方法
dispatchDraw(canvas);
...
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
//这里的dispatchDraw()是个空实现,我们可以看看ViewGruop的实现
protected void dispatchDraw(Canvas canvas) {
}
}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
protected void dispatchDraw(Canvas canvas) {
...
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
...
while (transientIndex >= 0) {
// there may be additional transient views after the normal views
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
//这里调用了drawChild()绘制子View的方法
more |= drawChild(canvas, transientChild, drawingTime);
}
...
}
//这里遍历调用了子view的draw()方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
}
ViewRootImpl
的performDraw()
->draw()
->drawSoftware()
,在其drawSoftware()
方法中调用了View
的draw()
方法,draw()
方法中调用了自身的onDraw()
方法,也调用了dispatchDraw()
方法对子View
进行遍历绘制,从ViewGroup
的dispatchDraw()
方法中调用了drawChild()
,依次对子view
进行了draw()
方法的绘制。