调用流程
performTraversals()里在调用完layout流程后也调用了performDraw()
ViewRootImpl # performDraw()
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
// mFullRedrawNeeded表示是否需要完全重绘
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
&& mAttachInfo.mThreadedRenderer.isEnabled()) {
usingAsyncReport = true;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
// TODO: Use the frame number
pendingDrawFinished();
});
}
try {
// 调用draw()
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// For whatever reason we didn't create a HardwareRenderer, end any
// hardware animations that are now dangling
if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interrupted!");
}
mWindowDrawCountDown = null;
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
if (LOCAL_LOGV) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
if (mSurfaceHolder != null && mSurface.isValid()) {
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else if (!usingAsyncReport) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
}
pendingDrawFinished();
}
}
}
ViewRootImpl # draw()
主要是处理绘制区域、坐标等准备工作
private boolean draw(boolean fullRedrawNeeded) {
...
// 获取需要绘制的区域
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
dirty.setEmpty();
if (animating && mScroller != null) {
mScroller.abortAnimation();
}
return false;
}
// 判断是否需要完全绘制
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
// 如果需要就将区域设置为屏幕的所有区域
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
// for instance) so we should just bail out. Locking the surface with software
// rendering at this point would lock it forever and prevent hardware renderer
// from doing its job when it comes back.
// Before we request a new frame we must however attempt to reinitiliaze the
// hardware renderer if it's in requested state. This would happen after an
// eglTerminate() for instance.
...
// 调用drawSoftware()绘制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
ViewRootImpl # drawSoftware()
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Draw with software renderer.
final Canvas canvas;
// We already have the offset of surfaceInsets in xoff, yoff and dirty region,
// therefore we need to add it back when moving the dirty region.
int dirtyXOffset = xoff;
int dirtyYOffset = yoff;
if (surfaceInsets != null) {
dirtyXOffset += surfaceInsets.left;
dirtyYOffset += surfaceInsets.top;
}
try {
dirty.offset(-dirtyXOffset, -dirtyYOffset);
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
// 创建一个绘制区域的canvas对象
canvas = mSurface.lockCanvas(dirty);
// The dirty rectangle can be modified by Surface.lockCanvas()
// noinspection ConstantConditions
// 判断lockCanvas有没有改变dirty的顶点值
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
// 设置画布密度
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
// Don't assume this is due to out of memory, it could be
// something else, and if it is something else then we could
// kill stuff (or ourself) for no reason.
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
} finally {
dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value.
}
try {
...
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
// 先清空画布
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
...
try {
// 设置画布偏移值
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
// 调用DecorView的draw()方法开始绘制流程
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
...
}
return true;
}
DecorView # draw()
@Override
public void draw(Canvas canvas) {
// 调用父类的draw()去绘制
super.draw(canvas);
// 如果有菜单背景的话就绘制
// private Drawable mMenuBackground
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
因为FrameLayout和ViewGroup都没有重写draw(),所以调用了View的draw()方法
View的draw()方法具体实现
注释中写的很清楚,一共分为六步:
- 绘制背景
- 存储图层(可跳过)
- 绘制自身
- 绘制子View
- 绘制阴影并恢复图层(可跳过)
- 绘制滚动条等效果
public void draw(Canvas canvas) {
...
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
...
}
绘制背景
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
// 如果不为透明
if (!dirtyOpaque) {
// 绘制背景
drawBackground(canvas);
}
View # drawBackground
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
// 根据View的四个布局参数来设置背景的边界位置
setBackgroundBounds();
// 硬件加速相关
...
final int scrollX = mScrollX;
final int scrollY = mScrollY;
// 判断View是否滑动
if ((scrollX | scrollY) == 0) {
// 没有滑动就直接绘制背景
background.draw(canvas);
} else {
// 滑动了就先移动canvas再绘制背景然后再把canvas移回去
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
View # setBackgroundBounds()
void setBackgroundBounds() {
if (mBackgroundSizeChanged && mBackground != null) {
// 设置背景图的四个坐标(就是View的layout过程中生成的顶点值)
mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
}
绘制自身
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
// 如果不需要绘制阴影,就可以跳过存储图层和绘制阴影的步骤
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
// 绘制自身
if (!dirtyOpaque) onDraw(canvas);
...
}
View # onDraw()
空实现,没有做统一实现,自定义View时需要自己去实现。
protected void onDraw(Canvas canvas) {
}
绘制子View
// 如果不需要绘制阴影,就可以跳过存储图层和绘制阴影的步骤
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
// 绘制自身
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
// 绘制子View
dispatchDraw(canvas);
}
View的dispatchDraw()为空方法,所以来看ViewGroup中的dispatchDraw()的实现
ViewGroup # dispatchDraw()
@Override
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
...
// Only use the preordered list if not HW accelerated, since the HW pipeline will do the
// draw reordering internally
final ArrayList<View> preorderedList = usingRenderNodeProperties
? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
// 遍历子View
for (int i = 0; i < childrenCount; i++) {
...
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
// 如果子View可见或者存在动画
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
// 调用drawChild()方法
more |= drawChild(canvas, child, drawingTime);
}
}
...
}
ViewGroup # drawChild()
直接调用了子View的draw()
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
// 此draw()方法和上面的不一样,这里有三个参数
return child.draw(canvas, this, drawingTime);
}
View # draw()
详讲
注释中也说了这个方法是ViewGroup.drawChild()调用去绘制子View的,和上面的draw()方法最大的不同就是这个draw()判断了是绘制缓存内容还是去调用一个参数的draw()绘制
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
*
* This is where the View specializes rendering behavior based on layer type,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...
// 如果不使用缓存(走GPU绘制)
if (!drawingWithDrawingCache) {
// 是否硬件加速(支持GPU绘制)
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// gpu绘制收集到的DisplayList
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// 如果需要跳过,就直接去调用分发给这个View的子View的方法
dispatchDraw(canvas);
} else {
// 调用普通的draw()
draw(canvas);
}
}
// 如果使用缓存且缓存不为空(走cpu绘制)
} else if (cache != null) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
// no layer paint, use temporary paint to draw bitmap
Paint cachePaint = parent.mCachePaint;
if (cachePaint == null) {
cachePaint = new Paint();
cachePaint.setDither(false);
parent.mCachePaint = cachePaint;
}
cachePaint.setAlpha((int) (alpha * 255));
// 绘制缓存内容
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
} else {
// use layer paint to draw the bitmap, merging the two alphas, but also restore
int layerPaintAlpha = mLayerPaint.getAlpha();
if (alpha < 1) {
mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
}
// 使用图层的Paint去绘制缓存内容
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
if (alpha < 1) {
mLayerPaint.setAlpha(layerPaintAlpha);
}
}
}
// 当ViewGroup向下分发绘制子view的时候,会根据是否开启硬件加速和view的绘制类型来判断view是用cpu绘制还是gpu绘制
// gpu的主要作用就是渲染图形,绘制图形的速度会高于cpu,但是gpu会比cpu更加耗电
...
return more;
}
这一步也可以归纳为ViewGroup绘制过程,它对子View进行了绘制,而子View又会调用自身的draw方法来绘制自身,这样不断遍历子View及子View的不断对自身的绘制,从而使得View树完成绘制。
绘制装饰
// 如果不需要绘制阴影,就可以跳过存储图层和绘制阴影的步骤
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
// 绘制自身
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
// 绘制子View
dispatchDraw(canvas);
...
// Step 6, draw decorations (foreground, scrollbars)
// 绘制装饰
onDrawForeground(canvas);
...
return;
}
View # onDrawForeground()
public void onDrawForeground(Canvas canvas) {
// 绘制滚动指示器
onDrawScrollIndicators(canvas);
// 绘制滚动条
onDrawScrollBars(canvas);
final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (foreground != null) {
...
// 如果有前景图片就绘制它
foreground.draw(canvas);
}
}
如果需要绘制边框阴影
如果要绘制阴影,就不会进入上面的方法块,它的步骤如下:
- 绘制背景
- 计算阴影带来的变量影响,对阴影分类去保存图层
- 绘制自身
- 绘制子View
- 分类去绘制阴影
- 加载保存的图层
- 绘制装饰
// 绘制背景
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 不需要绘制阴影的情况
if (!verticalEdges && !horizontalEdges) {
// ...
return;
}
// 如果需要绘制阴影
// 根据阴影情况去改变参数
...
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
// 保存图层
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
...
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 绘制自身
if (!dirtyOpaque) onDraw(canvas);
// 绘制子View
dispatchDraw(canvas);
...
// 绘制阴影
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
...
// 取出保存的图层
canvas.restoreToCount(saveCount);
...
// 绘制装饰
onDrawForeground(canvas);
时序图
View的setWillNotDraw()
如果一个View不需要绘制任何内容,设置这个标记位为true以后,系统会进行相应的优化。默认情况下,View没有启用这个标记位,但是ViewGroup会默认启用。如果ViewGroup需要通过onDraw绘制内容时,需要显式的关闭WILL_NOT_DRAW这个标记位。