Android高级UI开发(三十七)android Draw的绘制分发

 

 

android draw的绘制流程与measure测量流程类似,我们先来回顾一下measure的调用流程图,如下所示

今天我们要讲的是draw的绘制流程,所以直接从PerformDraw来开始分析(顺便纠正一下上图中的英文单词perfrom,应该是form).

这里就不贴ViewRootImpl.PerformDraw函数的源码了,通过看源码,它内部的调用过程如下:

 

ViewRootImpl.PerformDraw函数的源码了,通过看源码,它内部的调用过程如下:
 

 


  
  1. * 1. Draw the background
  2. * 2. If necessary, save the canvas' layers to prepare for fading
  3. * 3. Draw view's content
  4. * 4. Draw children
  5. * 5. If necessary, draw the fading edges and restore layers
  6. * 6. Draw decorations (scrollbars for instance)

上述6个步骤就是在View的draw(Canvas canvas)函数里调用的,我们在此贴出draw函数的代码:


  
  1. public void draw(Canvas canvas) {
  2. final int privateFlags = mPrivateFlags;
  3. final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
  4. (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
  5. mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
  6. /*
  7. * Draw traversal performs several drawing steps which must be executed
  8. * in the appropriate order:
  9. *
  10. * 1. Draw the background
  11. * 2. If necessary, save the canvas' layers to prepare for fading
  12. * 3. Draw view's content
  13. * 4. Draw children
  14. * 5. If necessary, draw the fading edges and restore layers
  15. * 6. Draw decorations (scrollbars for instance)
  16. */
  17. // Step 1, draw the background, if needed
  18. int saveCount;
  19. if (!dirtyOpaque) {
  20. drawBackground(canvas);
  21. }
  22. // skip step 2 & 5 if possible (common case)
  23. final int viewFlags = mViewFlags;
  24. boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
  25. boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
  26. if (!verticalEdges && !horizontalEdges) {
  27. // Step 3, draw the content
  28. if (!dirtyOpaque) onDraw(canvas);
  29. // Step 4, draw the children
  30. dispatchDraw(canvas);
  31. drawAutofilledHighlight(canvas);
  32. // Overlay is part of the content and draws beneath Foreground
  33. if (mOverlay != null && !mOverlay.isEmpty()) {
  34. mOverlay.getOverlayView().dispatchDraw(canvas);
  35. }
  36. // Step 6, draw decorations (foreground, scrollbars)
  37. onDrawForeground(canvas);
  38. // Step 7, draw the default focus highlight
  39. drawDefaultFocusHighlight(canvas);
  40. if (debugDraw()) {
  41. debugDrawFocus(canvas);
  42. }
  43. // we're done...
  44. return;
  45. }
  46. /*
  47. * Here we do the full fledged routine...
  48. * (this is an uncommon case where speed matters less,
  49. * this is why we repeat some of the tests that have been
  50. * done above)
  51. */
  52. boolean drawTop = false;
  53. boolean drawBottom = false;
  54. boolean drawLeft = false;
  55. boolean drawRight = false;
  56. float topFadeStrength = 0.0f;
  57. float bottomFadeStrength = 0.0f;
  58. float leftFadeStrength = 0.0f;
  59. float rightFadeStrength = 0.0f;
  60. // Step 2, save the canvas' layers
  61. int paddingLeft = mPaddingLeft;
  62. final boolean offsetRequired = isPaddingOffsetRequired();
  63. if (offsetRequired) {
  64. paddingLeft += getLeftPaddingOffset();
  65. }
  66. int left = mScrollX + paddingLeft;
  67. int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
  68. int top = mScrollY + getFadeTop(offsetRequired);
  69. int bottom = top + getFadeHeight(offsetRequired);
  70. if (offsetRequired) {
  71. right += getRightPaddingOffset();
  72. bottom += getBottomPaddingOffset();
  73. }
  74. final ScrollabilityCache scrollabilityCache = mScrollCache;
  75. final float fadeHeight = scrollabilityCache.fadingEdgeLength;
  76. int length = (int) fadeHeight;
  77. // clip the fade length if top and bottom fades overlap
  78. // overlapping fades produce odd-looking artifacts
  79. if (verticalEdges && (top + length > bottom - length)) {
  80. length = (bottom - top) / 2;
  81. }
  82. // also clip horizontal fades if necessary
  83. if (horizontalEdges && (left + length > right - length)) {
  84. length = (right - left) / 2;
  85. }
  86. if (verticalEdges) {
  87. topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
  88. drawTop = topFadeStrength * fadeHeight > 1.0f;
  89. bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
  90. drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
  91. }
  92. if (horizontalEdges) {
  93. leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
  94. drawLeft = leftFadeStrength * fadeHeight > 1.0f;
  95. rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
  96. drawRight = rightFadeStrength * fadeHeight > 1.0f;
  97. }
  98. saveCount = canvas.getSaveCount();
  99. int solidColor = getSolidColor();
  100. if (solidColor == 0) {
  101. final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
  102. if (drawTop) {
  103. canvas.saveLayer(left, top, right, top + length, null, flags);
  104. }
  105. if (drawBottom) {
  106. canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
  107. }
  108. if (drawLeft) {
  109. canvas.saveLayer(left, top, left + length, bottom, null, flags);
  110. }
  111. if (drawRight) {
  112. canvas.saveLayer(right - length, top, right, bottom, null, flags);
  113. }
  114. } else {
  115. scrollabilityCache.setFadeColor(solidColor);
  116. }
  117. // Step 3, draw the content
  118. if (!dirtyOpaque) onDraw(canvas);
  119. // Step 4, draw the children
  120. dispatchDraw(canvas);
  121. // Step 5, draw the fade effect and restore layers
  122. final Paint p = scrollabilityCache.paint;
  123. final Matrix matrix = scrollabilityCache.matrix;
  124. final Shader fade = scrollabilityCache.shader;
  125. if (drawTop) {
  126. matrix.setScale(1, fadeHeight * topFadeStrength);
  127. matrix.postTranslate(left, top);
  128. fade.setLocalMatrix(matrix);
  129. p.setShader(fade);
  130. canvas.drawRect(left, top, right, top + length, p);
  131. }
  132. if (drawBottom) {
  133. matrix.setScale(1, fadeHeight * bottomFadeStrength);
  134. matrix.postRotate(180);
  135. matrix.postTranslate(left, bottom);
  136. fade.setLocalMatrix(matrix);
  137. p.setShader(fade);
  138. canvas.drawRect(left, bottom - length, right, bottom, p);
  139. }
  140. if (drawLeft) {
  141. matrix.setScale(1, fadeHeight * leftFadeStrength);
  142. matrix.postRotate(-90);
  143. matrix.postTranslate(left, top);
  144. fade.setLocalMatrix(matrix);
  145. p.setShader(fade);
  146. canvas.drawRect(left, top, left + length, bottom, p);
  147. }
  148. if (drawRight) {
  149. matrix.setScale(1, fadeHeight * rightFadeStrength);
  150. matrix.postRotate(90);
  151. matrix.postTranslate(right, top);
  152. fade.setLocalMatrix(matrix);
  153. p.setShader(fade);
  154. canvas.drawRect(right - length, top, right, bottom, p);
  155. }
  156. canvas.restoreToCount(saveCount);
  157. drawAutofilledHighlight(canvas);
  158. // Overlay is part of the content and draws beneath Foreground
  159. if (mOverlay != null && !mOverlay.isEmpty()) {
  160. mOverlay.getOverlayView().dispatchDraw(canvas);
  161. }
  162. // Step 6, draw decorations (foreground, scrollbars)
  163. onDrawForeground(canvas);
  164. if (debugDraw()) {
  165. debugDrawFocus(canvas);
  166. }
  167. }

1. step 1 第一步 


  
  1. if (!dirtyOpaque) {
  2. drawBackground(canvas);
  3. }


不透明时,绘制背景。


// 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函数:


  
  1. @Override
  2. protected void dispatchDraw(Canvas canvas) {
  3. boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
  4. final int childrenCount = mChildrenCount;
  5. final View[] children = mChildren;
  6. int flags = mGroupFlags;
  7. if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
  8. final boolean buildCache = !isHardwareAccelerated();
  9. for (int i = 0; i < childrenCount; i++) {
  10. final View child = children[i];
  11. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
  12. final LayoutParams params = child.getLayoutParams();
  13. attachLayoutAnimationParameters(child, params, i, childrenCount);
  14. bindLayoutAnimation(child);
  15. }
  16. }
  17. final LayoutAnimationController controller = mLayoutAnimationController;
  18. if (controller.willOverlap()) {
  19. mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
  20. }
  21. controller.start();
  22. mGroupFlags &= ~FLAG_RUN_ANIMATION;
  23. mGroupFlags &= ~FLAG_ANIMATION_DONE;
  24. if (mAnimationListener != null) {
  25. mAnimationListener.onAnimationStart(controller.getAnimation());
  26. }
  27. }
  28. int clipSaveCount = 0;
  29. final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
  30. if (clipToPadding) {
  31. clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
  32. canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
  33. mScrollX + mRight - mLeft - mPaddingRight,
  34. mScrollY + mBottom - mTop - mPaddingBottom);
  35. }
  36. // We will draw our child's animation, let's reset the flag
  37. mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
  38. mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
  39. boolean more = false;
  40. final long drawingTime = getDrawingTime();
  41. if (usingRenderNodeProperties) canvas.insertReorderBarrier();
  42. final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
  43. int transientIndex = transientCount != 0 ? 0 : -1;
  44. // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
  45. // draw reordering internally
  46. final ArrayList<View> preorderedList = usingRenderNodeProperties
  47. ? null : buildOrderedChildList();
  48. final boolean customOrder = preorderedList == null
  49. && isChildrenDrawingOrderEnabled();
  50. for (int i = 0; i < childrenCount; i++) {
  51. while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
  52. final View transientChild = mTransientViews.get(transientIndex);
  53. if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
  54. transientChild.getAnimation() != null) {
  55. more |= drawChild(canvas, transientChild, drawingTime);
  56. }
  57. transientIndex++;
  58. if (transientIndex >= transientCount) {
  59. transientIndex = -1;
  60. }
  61. }
  62. final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
  63. final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
  64. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
  65. more |= drawChild(canvas, child, drawingTime);
  66. }
  67. }
  68. while (transientIndex >= 0) {
  69. // there may be additional transient views after the normal views
  70. final View transientChild = mTransientViews.get(transientIndex);
  71. if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
  72. transientChild.getAnimation() != null) {
  73. more |= drawChild(canvas, transientChild, drawingTime);
  74. }
  75. transientIndex++;
  76. if (transientIndex >= transientCount) {
  77. break;
  78. }
  79. }
  80. if (preorderedList != null) preorderedList.clear();
  81. // Draw any disappearing views that have animations
  82. if (mDisappearingChildren != null) {
  83. final ArrayList<View> disappearingChildren = mDisappearingChildren;
  84. final int disappearingCount = disappearingChildren.size() - 1;
  85. // Go backwards -- we may delete as animations finish
  86. for (int i = disappearingCount; i >= 0; i--) {
  87. final View child = disappearingChildren.get(i);
  88. more |= drawChild(canvas, child, drawingTime);
  89. }
  90. }
  91. if (usingRenderNodeProperties) canvas.insertInorderBarrier();
  92. if (debugDraw()) {
  93. onDebugDraw(canvas);
  94. }
  95. if (clipToPadding) {
  96. canvas.restoreToCount(clipSaveCount);
  97. }
  98. // mGroupFlags might have been updated by drawChild()
  99. flags = mGroupFlags;
  100. if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
  101. invalidate(true);
  102. }
  103. if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
  104. mLayoutAnimationController.isDone() && !more) {
  105. // We want to erase the drawing cache and notify the listener after the
  106. // next frame is drawn because one extra invalidate() is caused by
  107. // drawChild() after the animation is over
  108. mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
  109. final Runnable end = new Runnable() {
  110. @Override
  111. public void run() {
  112. notifyAnimationListener();
  113. }
  114. };
  115. post(end);
  116. }
  117. }


第一个for循环用于布局动画;第二个for循环绘制各子View,它内部调用了drawChild(canvas, child, drawingTime):


  
  1. /**
  2. * Draw one child of this View Group. This method is responsible for getting
  3. * the canvas in the right state. This includes clipping, translating so
  4. * that the child's scrolled origin is at 0, 0, and applying any animation
  5. * transformations.
  6. *
  7. * @param canvas The canvas on which to draw the child
  8. * @param child Who to draw
  9. * @param drawingTime The time at which draw is occurring
  10. * @return True if an invalidate() was issued
  11. */
  12. protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
  13. return child.draw(canvas, this, drawingTime);
  14. }

我们发现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

(完)