本篇文章基于 Android N - 7.1.1 主要分析下 draw 方法的执行流程;
1 回顾 在上面的 performTraversals 文章中,我们知道 perfomDraw 分为硬件绘制和软件绘制,这里我们只看软件绘制:
1.1 mHardwareRenderer.draw - 硬件绘制 我们先来看看硬件绘制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested; mInvalidateRootRequested = false ; mIsAnimating = false ; if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) { mHardwareYOffset = yOffset; mHardwareXOffset = xOffset; invalidateRoot = true ; } if (invalidateRoot) { mAttachInfo.mHardwareRenderer.invalidateRoot(); } dirty.setEmpty(); final boolean updated = updateContentDrawBounds(); if (mReportNextDraw) { mAttachInfo.mHardwareRenderer.setStopped(false ); } if (updated) { requestDrawWindow(); } mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this ); }
1.2 drawSoftware - 软件绘制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 private boolean drawSoftware (Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { final Canvas canvas; try { final int left = dirty.left; final int top = dirty.top; final int right = dirty.right; final int bottom = dirty.bottom; canvas = mSurface.lockCanvas(dirty); if (left != dirty.left || top != dirty.top || right != dirty.right || bottom != dirty.bottom) { attachInfo.mIgnoreDirtyState = true ; } canvas.setDensity(mDensity); } catch (Surface.OutOfResourcesException e) { handleOutOfResourcesException(e); return false ; } catch (IllegalArgumentException e) { Log.e(mTag, "Could not lock surface" , e); mLayoutRequested = true ; return false ; } try { if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v(mTag, "Surface " + surface + " drawing to bitmap w=" + canvas.getWidth() + ", h=" + canvas.getHeight()); } if (!canvas.isOpaque() || yoff != 0 || xoff != 0 ) { canvas.drawColor(0 , PorterDuff.Mode.CLEAR); } dirty.setEmpty(); mIsAnimating = false ; mView.mPrivateFlags |= View.PFLAG_DRAWN; if (DEBUG_DRAW) { Context cxt = mView.getContext(); Log.i(mTag, "Drawing: package:" + cxt.getPackageName() + ", metrics=" + cxt.getResources().getDisplayMetrics() + ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); } try { canvas.translate(-xoff, -yoff); if (mTranslator != null ) { mTranslator.translateCanvas(canvas); } canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0 ); attachInfo.mSetIgnoreDirtyState = false ; mView.draw(canvas); drawAccessibilityFocusedDrawableIfNeeded(canvas); } finally { if (!attachInfo.mSetIgnoreDirtyState) { attachInfo.mIgnoreDirtyState = false ; } } } finally { try { surface.unlockCanvasAndPost(canvas); } catch (IllegalArgumentException e) { Log.e(mTag, "Could not unlock surface" , e); mLayoutRequested = true ; return false ; } if (LOCAL_LOGV) { Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost" ); } } return true ; }
这里的关键代码就是:
这里的 mView 是 DecorView:
2 DecorView 2.1 draw DecorView 复写了 draw 方法,但是其内部依然是调用了父类 View 的 draw 方法:
1 2 3 4 5 6 7 8 9 @Override public void draw (Canvas canvas) { super .draw(canvas); if (mMenuBackground != null ) { mMenuBackground.draw(canvas); } }
2.2 onDraw DecorView 复写了 view 的 onDraw 方法,但是其内部主要逻辑依然是调用了父类的逻辑:
1 2 3 4 5 6 @Override public void onDraw (Canvas c) { super .onDraw(c); mBackgroundFallback.draw(mContentRoot, c, mWindow.mContentParent); }
3 ViewGroup 3.1 dispatchDraw - 核心 ViewGroup 复写了 view 的 dispatchDraw 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 @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); } 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 ; 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 ) { 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(); if (mDisappearingChildren != null ) { final ArrayList<View> disappearingChildren = mDisappearingChildren; final int disappearingCount = disappearingChildren.size() - 1 ; 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); } 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) { mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; final Runnable end = new Runnable () { @Override public void run () { notifyAnimationListener(); } }; post(end); } }
3.1.1 buildOrderedChildList 该方法会返回 child view 的预排序列表 mPreSortedChildren,首先按 Z 排序,然后按 child view 绘制顺序排序(如果适用)。
使用后必须清除此列表,以免泄漏子视图。
使用稳定的插入排序,通常为 O(n)时间复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ArrayList<View> buildOrderedChildList () { final int childrenCount = mChildrenCount; if (childrenCount <= 1 || !hasChildWithZ()) return null ; if (mPreSortedChildren == null ) { mPreSortedChildren = new ArrayList <>(childrenCount); } else { mPreSortedChildren.clear(); mPreSortedChildren.ensureCapacity(childrenCount); } final boolean customOrder = isChildrenDrawingOrderEnabled(); for (int i = 0 ; i < childrenCount; i++) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View nextChild = mChildren[childIndex]; final float currentZ = nextChild.getZ(); int insertIndex = i; while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1 ).getZ() > currentZ) { insertIndex--; } mPreSortedChildren.add(insertIndex, nextChild); } return mPreSortedChildren; }
每个 view 内部都有一个,它包含 View 属性(软硬都用),也可能包含 View 内容的 DisplayList(硬件加速使用)。
我们看看 hasChildWithZ :
1 2 3 4 5 6 7 8 9 10 private View[] mChildren; private boolean hasChildWithZ () { for (int i = 0 ; i < mChildrenCount; i++) { if (mChildren[i].getZ() != 0 ) return true ; } return false ; }
判断该 View 的是否有可视 z 位置(以像素为单位)。该方法等价于 setTranslationZ + getElevation 属性;
1 2 3 4 @ViewDebug .ExportedProperty(category = "drawing" )public float getZ () { return getElevation() + getTranslationZ(); }
也就是 android:elevation 和 android:translationZ
3.1.1.1 isChildrenDrawingOrderEnabled 判断下 ViewGroup 是否按照 getChildDrawingOrder 返回的方式绘制 view:
1 2 3 4 @ViewDebug .ExportedProperty(category = "drawing" )protected boolean isChildrenDrawingOrderEnabled () { return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; }
3.1.2 getAndVerifyPreorderedIndex 获取 view 实际绘制顺序;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private int getAndVerifyPreorderedIndex (int childrenCount, int i, boolean customOrder) { final int childIndex; if (customOrder) { final int childIndex1 = getChildDrawingOrder(childrenCount, i); if (childIndex1 >= childrenCount) { throw new IndexOutOfBoundsException ("getChildDrawingOrder() " + "returned invalid index " + childIndex1 + " (child count is " + childrenCount + ")" ); } childIndex = childIndex1; } else { childIndex = i; } return childIndex; }
3.1.3 getChildDrawingOrder 修改绘制顺序,如果要更改子代的绘制顺序,ViewGroup 要重写此方法。默认情况下,它返回 drawingPosition!
想要这个方法生效,那么就要先调用:setChildrenDrawingOrderEnabled
参数:drawingPosition,当前的绘制顺序;
1 2 3 protected int getChildDrawingOrder (int childCount, int drawingPosition) { return drawingPosition; }
3.2 drawChild - 核心 绘制 child view。
这个是直接翻译的注视:此方法负责使画布处于正确的状态。这包括剪切,平移以使孩子的滚动原点位于 0、0,并应用任何动画转换。
可以看出子至少有个作用:
1、修正 canvas 的自己状态;2、动画处理;
1 2 3 4 protected boolean drawChild (Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this , drawingTime); }
绘制 child 的逻辑是在 view 的 draw[3] 里面;
3.3 dispatchGetDisplayList - 核心 ViewGroup 通知 child view 重新创建自身的 DisplayList:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override protected void dispatchGetDisplayList () { final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0 ; i < count; i++) { final View child = children[i]; if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null )) { recreateChildDisplayList(child); } } if (mOverlay != null ) { View overlayView = mOverlay.getOverlayView(); recreateChildDisplayList(overlayView); } if (mDisappearingChildren != null ) { final ArrayList<View> disappearingChildren = mDisappearingChildren; final int disappearingCount = disappearingChildren.size(); for (int i = 0 ; i < disappearingCount; ++i) { final View child = disappearingChildren.get(i); recreateChildDisplayList(child); } } }
3.3.1 recreateChildDisplayList 1 2 3 4 5 6 7 private void recreateChildDisplayList (View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0 ; child.mPrivateFlags &= ~PFLAG_INVALIDATED; child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false ; }
4 View 4.1 draw[1] - 核心 View 的方法是最核心的部分:
将 View(及其所有 child)绘制(渲染)到给定的 Canvas。 在调用此函数之前,View 必须完成测量和布局。
自定义 View 时,请实现 onDraw 而不要重写 draw(draw 方法会调用 onDraw)。
如果确实需要重写 draw,一定要调用 super.draw!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 @CallSuper 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; int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0 ; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0 ; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) onDraw(canvas); dispatchDraw(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } onDrawForeground(canvas); return ; } 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 ; 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; if (verticalEdges && (top + length > bottom - length)) { length = (bottom - top) / 2 ; } 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); } if (!dirtyOpaque) onDraw(canvas); dispatchDraw(canvas); 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); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } onDrawForeground(canvas); }
从注视我们可以看出,绘制过程有如下的绘制步骤,这些绘制步骤必须以适当的顺序执行:
1、绘制背景 2、如有必要,保存画布的图层以准备淡入淡出 3、绘制视图的内容 4、绘制 child 5、如有必要,绘制褪色边缘并还原图层 6、绘制装饰(例如滚动条)
对于增加了淡入淡出效果情况,也是很类似,只不过增加了 savelayer 和 restore 的操作;
4.1.1 drawBackground 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private void drawBackground (Canvas canvas) { final Drawable background = mBackground; if (background == null ) { return ; } setBackgroundBounds(); if (canvas.isHardwareAccelerated() && mAttachInfo != null && mAttachInfo.mHardwareRenderer != null ) { mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode); final RenderNode renderNode = mBackgroundRenderNode; if (renderNode != null && renderNode.isValid()) { setBackgroundRenderNodeProperties(renderNode); ((DisplayListCanvas) canvas).drawRenderNode(renderNode); return ; } } final int scrollX = mScrollX; final int scrollY = mScrollY; if ((scrollX | scrollY) == 0 ) { background.draw(canvas); } else { canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX, -scrollY); } }
不多说了;
4.1.2 onDraw - 核心 默认是空方法,需要自己实现;
1 2 protected void onDraw (Canvas canvas) {}
这个方法是绘制 background 的;
4.1.3 dispatchDraw - 核心 默认是空方法,需要自己实现;
这个方法由 draw 调用,用于绘制 child view,一般来说 view group 肯定是需要重写这个方法,在 onDraw 方法调用后再调用;
1 2 3 protected void dispatchDraw (Canvas canvas) {}
4.1.4 onDrawForeground 绘制 view 的所有前景内容。
前景内容可能包括滚动条、可绘制对象或其他特定于视图的装饰。前景会被绘制在 view 内容的顶部。
前景可以通过 setForeground 方法设置;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void onDrawForeground (Canvas canvas) { onDrawScrollIndicators(canvas); onDrawScrollBars(canvas); final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null ; if (foreground != null ) { if (mForegroundInfo.mBoundsChanged) { mForegroundInfo.mBoundsChanged = false ; final Rect selfBounds = mForegroundInfo.mSelfBounds; final Rect overlayBounds = mForegroundInfo.mOverlayBounds; if (mForegroundInfo.mInsidePadding) { selfBounds.set(0 , 0 , getWidth(), getHeight()); } else { selfBounds.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); } final int ld = getLayoutDirection(); Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld); foreground.setBounds(overlayBounds); } foreground.draw(canvas); } }
4.2 draw[3] - 核心 这个方法由 ViewGroup.drawChild() 触发,绘制 child;
其实可以看到,无论是软件绘制,还是硬件绘制,都会进入这个方法,最终都会调用到draw(Canvas canvas)
方法来绘制,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 boolean draw (Canvas canvas, ViewGroup parent, long drawingTime) { final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); boolean drawingWithRenderNode = mAttachInfo != null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; boolean more = false ; final boolean childHasIdentityMatrix = hasIdentityMatrix(); final int parentFlags = parent.mGroupFlags; if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0 ) { parent.getChildTransformation().clear(); parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION; } Transformation transformToApply = null ; boolean concatMatrix = false ; final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired; final Animation a = getAnimation(); if (a != null ) { more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); concatMatrix = a.willChangeTransformationMatrix(); if (concatMatrix) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } transformToApply = parent.getChildTransformation(); } else { if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0 ) { mRenderNode.setAnimationMatrix(null ); mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } if (!drawingWithRenderNode && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0 ) { final Transformation t = parent.getChildTransformation(); final boolean hasTransform = parent.getChildStaticTransformation(this , t); if (hasTransform) { final int transformType = t.getTransformationType(); transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null ; concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0 ; } } } concatMatrix |= !childHasIdentityMatrix; mPrivateFlags |= PFLAG_DRAWN; if (!concatMatrix && (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN && canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) && (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0 ) { mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED; return more; } mPrivateFlags2 &= ~PFLAG2_VIEW_QUICK_REJECTED; if (hardwareAcceleratedCanvas) { mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0 ; mPrivateFlags &= ~PFLAG_INVALIDATED; } RenderNode renderNode = null ; Bitmap cache = null ; int layerType = getLayerType(); if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) { if (layerType != LAYER_TYPE_NONE) { layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true ); } cache = getDrawingCache(true ); } if (drawingWithRenderNode) { renderNode = updateDisplayListIfDirty(); if (!renderNode.isValid()) { renderNode = null ; drawingWithRenderNode = false ; } } int sx = 0 ; int sy = 0 ; if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; } final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; final boolean offsetForScroll = cache == null && !drawingWithRenderNode; int restoreTo = -1 ; if (!drawingWithRenderNode || transformToApply != null ) { restoreTo = canvas.save(); } if (offsetForScroll) { canvas.translate(mLeft - sx, mTop - sy); } else { if (!drawingWithRenderNode) { canvas.translate(mLeft, mTop); } if (scalingRequired) { if (drawingWithRenderNode) { restoreTo = canvas.save(); } final float scale = 1.0f / mAttachInfo.mApplicationScale; canvas.scale(scale, scale); } } float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha()); if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0 ) { if (transformToApply != null || !childHasIdentityMatrix) { int transX = 0 ; int transY = 0 ; if (offsetForScroll) { transX = -sx; transY = -sy; } if (transformToApply != null ) { if (concatMatrix) { if (drawingWithRenderNode) { renderNode.setAnimationMatrix(transformToApply.getMatrix()); } else { canvas.translate(-transX, -transY); canvas.concat(transformToApply.getMatrix()); canvas.translate(transX, transY); } parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; } float transformAlpha = transformToApply.getAlpha(); if (transformAlpha < 1 ) { alpha *= transformAlpha; parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; } } if (!childHasIdentityMatrix && !drawingWithRenderNode) { canvas.translate(-transX, -transY); canvas.concat(getMatrix()); canvas.translate(transX, transY); } } if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0 ) { if (alpha < 1 ) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA; } else { mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA; } parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; if (!drawingWithDrawingCache) { final int multipliedAlpha = (int ) (255 * alpha); if (!onSetAlpha(multipliedAlpha)) { if (drawingWithRenderNode) { renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha()); } else if (layerType == LAYER_TYPE_NONE) { canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha); } } else { mPrivateFlags |= PFLAG_ALPHA_SET; } } } } else if ((mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) { onSetAlpha(255 ); mPrivateFlags &= ~PFLAG_ALPHA_SET; } if (!drawingWithRenderNode) { if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 && cache == null ) { if (offsetForScroll) { canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight()); } else { if (!scalingRequired || cache == null ) { canvas.clipRect(0 , 0 , getWidth(), getHeight()); } else { canvas.clipRect(0 , 0 , cache.getWidth(), cache.getHeight()); } } } if (mClipBounds != null ) { canvas.clipRect(mClipBounds); } } if (!drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } else if (cache != null ) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; if (layerType == LAYER_TYPE_NONE || mLayerPaint == null ) { 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 { int layerPaintAlpha = mLayerPaint.getAlpha(); if (alpha < 1 ) { mLayerPaint.setAlpha((int ) (alpha * layerPaintAlpha)); } canvas.drawBitmap(cache, 0.0f , 0.0f , mLayerPaint); if (alpha < 1 ) { mLayerPaint.setAlpha(layerPaintAlpha); } } } if (restoreTo >= 0 ) { canvas.restoreToCount(restoreTo); } if (a != null && !more) { if (!hardwareAcceleratedCanvas && !a.getFillAfter()) { onSetAlpha(255 ); } parent.finishAnimatingView(this , a); } if (more && hardwareAcceleratedCanvas) { if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) { invalidate(true ); } } mRecreateDisplayList = false ; return more; }
可以看到:
对于软件绘制:
会有一个叫绘制缓存的概念,buildDrawingCache 负责创建绘制缓存,view 会将自身绘制到缓存上,然后再绘制到 parent 的 canvas 上;
如果不使用绘制缓存的话,就会直接绘制到 parent 传进来的 canvas 上;
对于硬件绘制:
会有一个叫 RenderBode 和 DisplayList 的概念;
4.2.1 updateDisplayListIfDirty 获取 View 的 RenderNode,同时更新显示列表 DisplayList;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 @NonNull public RenderNode updateDisplayListIfDirty () { final RenderNode renderNode = mRenderNode; if (!canHaveDisplayList()) { return renderNode; } if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; } mRecreateDisplayList = true ; int width = mRight - mLeft; int height = mBottom - mTop; int layerType = getLayerType(); final DisplayListCanvas canvas = renderNode.start(width, height); canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { if (layerType == LAYER_TYPE_SOFTWARE) { buildDrawingCache(true ); Bitmap cache = getDrawingCache(true ); if (cache != null ) { canvas.drawBitmap(cache, 0 , 0 , mLayerPaint); } } else { computeScroll(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } } else { draw(canvas); } } } finally { renderNode.end(canvas); setDisplayListProperties(renderNode); } } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; }
这里有几个标志位:
1 mRecreateDisplayList = false ;
表示 View 在当前图形绘制之前被标记为 INVALIDATED 或者起 DisplayList 无效。
如果为 true,则 View 必须重新计算其 DisplayList,仅在硬件加速时使用。
系统会清除 INVALIDATED 标志,同时将是否 INVALIDATED 保存到该变量中,硬件加速后续会用到
( 比如在 getDisplayList() 时和在 drawChild() 中,当我们决定将视图的子级显示列表绘制到我们自己的状态中)。
4.2.2 buildDrawingCache 构建绘制缓存,参数 autoScale 表示是否自动缩放;
如果未启用自动缩放,则此方法将创建与该 view 相同大小的 bitmap 缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void buildDrawingCache (boolean autoScale) { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null )) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "buildDrawingCache/SW Layer for " + getClass().getSimpleName()); } try { buildDrawingCacheImpl(autoScale); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } }
如果图形缓存无效,则强制构建图形缓存,如果只调用 buildDrawingCache() 而没有调用 setDrawingCacheEnabled(true) 则应在之后调用 destroyDrawingCache();
启用硬件加速后,不应使用此方法。如果不需要图形缓存,则调用此方法将增加内存使用量,并使 view 一次性通过软件绘制出来,从而对性能产生负面影响。
4.2.2.1 buildDrawingCacheImpl 构建绘制缓存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 private void buildDrawingCacheImpl (boolean autoScale) { mCachingFailed = false ; int width = mRight - mLeft; int height = mBottom - mTop; final AttachInfo attachInfo = mAttachInfo; final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; if (autoScale && scalingRequired) { width = (int ) ((width * attachInfo.mApplicationScale) + 0.5f ); height = (int ) ((height * attachInfo.mApplicationScale) + 0.5f ); } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque(); final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache; final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4 ); final long drawingCacheSize = ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize(); if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) { if (width > 0 && height > 0 ) { Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is" + " too large to fit into a software layer (or drawing cache), needs " + projectedBitmapSize + " bytes, only " + drawingCacheSize + " available" ); } destroyDrawingCache(); mCachingFailed = true ; return ; } boolean clear = true ; Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache; if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { Bitmap.Config quality; if (!opaque) { switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { case DRAWING_CACHE_QUALITY_AUTO: case DRAWING_CACHE_QUALITY_LOW: case DRAWING_CACHE_QUALITY_HIGH: default : quality = Bitmap.Config.ARGB_8888; break ; } } else { quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; } if (bitmap != null ) bitmap.recycle(); try { bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(), width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if (autoScale) { mDrawingCache = bitmap; } else { mUnscaledDrawingCache = bitmap; } if (opaque && use32BitCache) bitmap.setHasAlpha(false ); } catch (OutOfMemoryError e) { if (autoScale) { mDrawingCache = null ; } else { mUnscaledDrawingCache = null ; } mCachingFailed = true ; return ; } clear = drawingCacheBackgroundColor != 0 ; } Canvas canvas; if (attachInfo != null ) { canvas = attachInfo.mCanvas; if (canvas == null ) { canvas = new Canvas (); } canvas.setBitmap(bitmap); attachInfo.mCanvas = null ; } else { canvas = new Canvas (bitmap); } if (clear) { bitmap.eraseColor(drawingCacheBackgroundColor); } computeScroll(); final int restoreCount = canvas.save(); if (autoScale && scalingRequired) { final float scale = attachInfo.mApplicationScale; canvas.scale(scale, scale); } canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= PFLAG_DRAWN; if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated || mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID; } if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } } else { draw(canvas); } canvas.restoreToCount(restoreCount); canvas.setBitmap(null ); if (attachInfo != null ) { attachInfo.mCanvas = canvas; } }
4.2.3 getDrawingCache 返回一个绘制缓存:
1 2 3 4 5 6 7 8 9 10 11 12 public Bitmap getDrawingCache (boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null ; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { buildDrawingCache(autoScale); } return autoScale ? mDrawingCache : mUnscaledDrawingCache; }
4.3 dispatchGetDisplayList - 核心 ViewGroup 使用该方法来还原或重新创建 child view 的显示列表。
常规的 draw/dispatchDraw 过程中,当 ViewGroup 不需要重新创建自己的显示列表时,getDisplayList() 会调用此方法;
1 protected void dispatchGetDisplayList () {}
可以看到。真正的实现在 ViewGroup 中。
5 ThreadedRenderer - 硬件绘制简单记录 硬件绘制的操作要比软件绘制复杂的多,这里只简单的分析下:
5.1 draw - 核心 我们来看看硬件绘制的流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void draw (View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { attachInfo.mIgnoreDirtyState = true ; final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; choreographer.mFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); attachInfo.mIgnoreDirtyState = false ; if (attachInfo.mPendingAnimatingRenderNodes != null ) { final int count = attachInfo.mPendingAnimatingRenderNodes.size(); for (int i = 0 ; i < count; i++) { registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i)); } attachInfo.mPendingAnimatingRenderNodes.clear(); attachInfo.mPendingAnimatingRenderNodes = null ; } final long [] frameInfo = choreographer.mFrameInfo.mFrameInfo; int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0 ) { setEnabled(false ); attachInfo.mViewRootImpl.mSurface.release(); attachInfo.mViewRootImpl.invalidate(); } if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0 ) { attachInfo.mViewRootImpl.invalidate(); } }
硬件绘制的 canvas 它是一个 DisplayListCanvas 对象,它的每一个 drawxxx 的方法并不是真正的绘制,而是在记录绘制的操作;
和软件绘制绘制类似,由于 View 体系是一个树形结构,所以硬件绘制也要遍历这个 tree,但是他的遍历是记录每个 view 的绘制操作,而不是直接绘制;
而触发这个遍历的方法就是:updateRootDisplayList,updateViewTreeDisplayList 还有 updateDisplayListIfDirty 方法;
5.2 updateRootDisplayList - 核心 继续看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private void updateRootDisplayList (View view, HardwareDrawCallbacks callbacks) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()" ); updateViewTreeDisplayList(view); if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onHardwarePreDraw(canvas); canvas.insertReorderBarrier(); canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.insertInorderBarrier(); callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false ; } finally { mRootNode.end(canvas); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW); }
继续看:
5.3 updateViewTreeDisplayList - 核心 1 2 3 4 5 6 7 8 9 private void updateViewTreeDisplayList (View view) { view.mPrivateFlags |= View.PFLAG_DRAWN; view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)== View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; view.updateDisplayListIfDirty(); view.mRecreateDisplayList = false ; }
6 总结 我们用一张图来总结下整个的软件绘制的流程;
最近加班多,要休息下~~~(略)