android draw的绘制流程与measure测量流程类似,我们先来回顾一下measure的调用流程图,如下所示
今天我们要讲的是draw的绘制流程,所以直接从PerformDraw来开始分析(顺便纠正一下上图中的英文单词perfrom,应该是form).
这里就不贴ViewRootImpl.PerformDraw函数的源码了,通过看源码,它内部的调用过程如下:
ViewRootImpl.PerformDraw函数的源码了,通过看源码,它内部的调用过程如下:
-
* 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)
上述6个步骤就是在View的draw(Canvas canvas)函数里调用的,我们在此贴出draw函数的代码:
-
public void draw(Canvas canvas) {
-
final int privateFlags = mPrivateFlags;
-
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
-
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
-
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
-
-
/*
-
* 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)
-
*/
-
-
// Step 1, draw the background, if needed
-
int saveCount;
-
-
if (!dirtyOpaque) {
-
drawBackground(canvas);
-
}
-
-
// 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);
-
-
// Step 4, draw the children
-
dispatchDraw(canvas);
-
-
drawAutofilledHighlight(canvas);
-
-
// Overlay is part of the content and draws beneath Foreground
-
if (mOverlay != null && !mOverlay.isEmpty()) {
-
mOverlay.getOverlayView().dispatchDraw(canvas);
-
}
-
-
// Step 6, draw decorations (foreground, scrollbars)
-
onDrawForeground(canvas);
-
-
// Step 7, draw the default focus highlight
-
drawDefaultFocusHighlight(canvas);
-
-
if (debugDraw()) {
-
debugDrawFocus(canvas);
-
}
-
-
// we're done...
-
return;
-
}
-
-
/*
-
* Here we do the full fledged routine...
-
* (this is an uncommon case where speed matters less,
-
* this is why we repeat some of the tests that have been
-
* done above)
-
*/
-
-
boolean drawTop = false;
-
boolean drawBottom = false;
-
boolean drawLeft = false;
-
boolean drawRight = false;
-
-
float topFadeStrength = 0.0f;
-
float bottomFadeStrength = 0.0f;
-
float leftFadeStrength = 0.0f;
-
float rightFadeStrength = 0.0f;
-
-
// Step 2, save the canvas' layers
-
int paddingLeft = mPaddingLeft;
-
-
final boolean offsetRequired = isPaddingOffsetRequired();
-
if (offsetRequired) {
-
paddingLeft += getLeftPaddingOffset();
-
}
-
-
int left = mScrollX + paddingLeft;
-
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
-
int top = mScrollY + getFadeTop(offsetRequired);
-
int bottom = top + getFadeHeight(offsetRequired);
-
-
if (offsetRequired) {
-
right += getRightPaddingOffset();
-
bottom += getBottomPaddingOffset();
-
}
-
-
final ScrollabilityCache scrollabilityCache = mScrollCache;
-
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
-
int length = (int) fadeHeight;
-
-
// clip the fade length if top and bottom fades overlap
-
// overlapping fades produce odd-looking artifacts
-
if (verticalEdges && (top + length > bottom - length)) {
-
length = (bottom - top) / 2;
-
}
-
-
// also clip horizontal fades if necessary
-
if (horizontalEdges && (left + length > right - length)) {
-
length = (right - left) / 2;
-
}
-
-
if (verticalEdges) {
-
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
-
drawTop = topFadeStrength * fadeHeight > 1.0f;
-
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
-
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
-
}
-
-
if (horizontalEdges) {
-
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
-
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
-
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
-
drawRight = rightFadeStrength * fadeHeight > 1.0f;
-
}
-
-
saveCount = canvas.getSaveCount();
-
-
int solidColor = getSolidColor();
-
if (solidColor == 0) {
-
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-
-
if (drawTop) {
-
canvas.saveLayer(left, top, right, top + length, null, flags);
-
}
-
-
if (drawBottom) {
-
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
-
}
-
-
if (drawLeft) {
-
canvas.saveLayer(left, top, left + length, bottom, null, flags);
-
}
-
-
if (drawRight) {
-
canvas.saveLayer(right - length, top, right, bottom, null, flags);
-
}
-
} else {
-
scrollabilityCache.setFadeColor(solidColor);
-
}
-
-
// Step 3, draw the content
-
if (!dirtyOpaque) onDraw(canvas);
-
-
// Step 4, draw the children
-
dispatchDraw(canvas);
-
-
// Step 5, draw the fade effect and restore layers
-
final Paint p = scrollabilityCache.paint;
-
final Matrix matrix = scrollabilityCache.matrix;
-
final Shader fade = scrollabilityCache.shader;
-
-
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);
-
}
-
-
if (drawBottom) {
-
matrix.setScale(1, fadeHeight * bottomFadeStrength);
-
matrix.postRotate(180);
-
matrix.postTranslate(left, bottom);
-
fade.setLocalMatrix(matrix);
-
p.setShader(fade);
-
canvas.drawRect(left, bottom - length, right, bottom, p);
-
}
-
-
if (drawLeft) {
-
matrix.setScale(1, fadeHeight * leftFadeStrength);
-
matrix.postRotate(-90);
-
matrix.postTranslate(left, top);
-
fade.setLocalMatrix(matrix);
-
p.setShader(fade);
-
canvas.drawRect(left, top, left + length, bottom, p);
-
}
-
-
if (drawRight) {
-
matrix.setScale(1, fadeHeight * rightFadeStrength);
-
matrix.postRotate(90);
-
matrix.postTranslate(right, top);
-
fade.setLocalMatrix(matrix);
-
p.setShader(fade);
-
canvas.drawRect(right - length, top, right, bottom, p);
-
}
-
-
canvas.restoreToCount(saveCount);
-
-
drawAutofilledHighlight(canvas);
-
-
// Overlay is part of the content and draws beneath Foreground
-
if (mOverlay != null && !mOverlay.isEmpty()) {
-
mOverlay.getOverlayView().dispatchDraw(canvas);
-
}
-
-
// Step 6, draw decorations (foreground, scrollbars)
-
onDrawForeground(canvas);
-
-
if (debugDraw()) {
-
debugDrawFocus(canvas);
-
}
-
}
1. step 1 第一步
-
-
if (!dirtyOpaque) {
-
drawBackground(canvas);
-
}
不透明时,绘制背景。
// skip step 2 & 5 if possible (common case)
这条注释表示通常情况下step2和step5是不需要的,那我们直接看step3:draw the content.
2. step 3 第三步
if (!dirtyOpaque) onDraw(canvas);不透明时,绘制内容,通常见我们自定义View需要重写onDraw函数。
3. step 4 第四步
dispatchDraw(canvas);
绘制分发,主要是绘制当前父容器的子View,然后各个子View也会调用它自己的draw方法,即递归调用当前的draw方法,当然如果子View正好也是一个ViewGroup的话,它又会去调用dispatchDraw(canvas)方法,
以此类推,直到整个递归完成;如果子View不是一个ViewGroup容器,那就直接调用View.dispatchDraw,我们定位到View.dispatchDraw发现它是一个空方法,如下:
/**
* Called by draw to draw the child views. This may be overridden
* by derived classes to gain control just before its children are drawn
* (but after its own view has been drawn).
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(Canvas canvas) {
}
通过注释可以看到,这个函数通常是被View的子类重写,用于绘制容器内部的子View的。这里我们可以把View的子类当作ViewGroup,在ViewGroup里会重写dispatchDraw这个方法,用来绘制child。
以下是ViewGroup重写后的dispatchDraw函数:
-
@Override
-
protected void dispatchDraw(Canvas canvas) {
-
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
-
final int childrenCount = mChildrenCount;
-
final View[] children = mChildren;
-
int flags = mGroupFlags;
-
-
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
-
final boolean buildCache = !isHardwareAccelerated();
-
for (int i = 0; i < childrenCount; i++) {
-
final View child = children[i];
-
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
-
final LayoutParams params = child.getLayoutParams();
-
attachLayoutAnimationParameters(child, params, i, childrenCount);
-
bindLayoutAnimation(child);
-
}
-
}
-
-
final LayoutAnimationController controller = mLayoutAnimationController;
-
if (controller.willOverlap()) {
-
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
-
}
-
-
controller.start();
-
-
mGroupFlags &= ~FLAG_RUN_ANIMATION;
-
mGroupFlags &= ~FLAG_ANIMATION_DONE;
-
-
if (mAnimationListener != null) {
-
mAnimationListener.onAnimationStart(controller.getAnimation());
-
}
-
}
-
-
int clipSaveCount = 0;
-
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
-
if (clipToPadding) {
-
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
-
mScrollX + mRight - mLeft - mPaddingRight,
-
mScrollY + mBottom - mTop - mPaddingBottom);
-
}
-
-
// We will draw our child's animation, let's reset the flag
-
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
-
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
-
-
boolean more = false;
-
final long drawingTime = getDrawingTime();
-
-
if (usingRenderNodeProperties) canvas.insertReorderBarrier();
-
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
-
int transientIndex = transientCount != 0 ? 0 : -1;
-
// 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();
-
for (int i = 0; i < childrenCount; i++) {
-
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
-
final View transientChild = mTransientViews.get(transientIndex);
-
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
-
transientChild.getAnimation() != null) {
-
more |= drawChild(canvas, transientChild, drawingTime);
-
}
-
transientIndex++;
-
if (transientIndex >= transientCount) {
-
transientIndex = -1;
-
}
-
}
-
-
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
-
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
-
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
-
more |= drawChild(canvas, child, drawingTime);
-
}
-
}
-
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) {
-
more |= drawChild(canvas, transientChild, drawingTime);
-
}
-
transientIndex++;
-
if (transientIndex >= transientCount) {
-
break;
-
}
-
}
-
if (preorderedList != null) preorderedList.clear();
-
-
// Draw any disappearing views that have animations
-
if (mDisappearingChildren != null) {
-
final ArrayList<View> disappearingChildren = mDisappearingChildren;
-
final int disappearingCount = disappearingChildren.size() - 1;
-
// Go backwards -- we may delete as animations finish
-
for (int i = disappearingCount; i >= 0; i--) {
-
final View child = disappearingChildren.get(i);
-
more |= drawChild(canvas, child, drawingTime);
-
}
-
}
-
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
-
-
if (debugDraw()) {
-
onDebugDraw(canvas);
-
}
-
-
if (clipToPadding) {
-
canvas.restoreToCount(clipSaveCount);
-
}
-
-
// mGroupFlags might have been updated by drawChild()
-
flags = mGroupFlags;
-
-
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
-
invalidate(true);
-
}
-
-
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
-
mLayoutAnimationController.isDone() && !more) {
-
// We want to erase the drawing cache and notify the listener after the
-
// next frame is drawn because one extra invalidate() is caused by
-
// drawChild() after the animation is over
-
mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
-
final Runnable end = new Runnable() {
-
@Override
-
public void run() {
-
notifyAnimationListener();
-
}
-
};
-
post(end);
-
}
-
}
第一个for循环用于布局动画;第二个for循环绘制各子View,它内部调用了drawChild(canvas, child, drawingTime):
-
/**
-
* Draw one child of this View Group. This method is responsible for getting
-
* the canvas in the right state. This includes clipping, translating so
-
* that the child's scrolled origin is at 0, 0, and applying any animation
-
* transformations.
-
*
-
* @param canvas The canvas on which to draw the child
-
* @param child Who to draw
-
* @param drawingTime The time at which draw is occurring
-
* @return True if an invalidate() was issued
-
*/
-
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-
return child.draw(canvas, this, drawingTime);
-
}
我们发现drawChild又调用了child.draw,即View中的draw函数,我们发现这个child.draw函数的参数和之前不一样,但是它内部还是调用了我们之前的View.draw(Canvas canvas),
来绘制自己的内容及递归分发绘制子View。
4. step6. 第六步 绘制装饰
onDrawForeground(canvas)
这里主要绘制滚动条、及各View的foreGround属性设置的图片,颜色值、水波纹等。
Ok,draw绘制流程就聊到这里,其实它与layout、measure都有一个共性,就是递归View树。
文章来源: blog.csdn.net,作者:冉航--小虾米,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/gaoxiaoweiandy/article/details/103655089