带着问题看源码
再接再厉,我们来分析UI绘制流程最后一步绘制流程
入口ViewRootImpl.performDraw()方法
private void performDraw() { //... try { draw(fullRedrawNeeded); } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } //... } //调用了draw()方法,参数表示是否需要重新绘制全部视图 private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; //... //控件重绘 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; } //fullRedrawNeeded为true,表示需要绘制整个区域 if (fullRedrawNeeded) { mAttachInfo.mIgnoreDirtyState = true; dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); } mAttachInfo.mTreeObserver.dispatchOnDraw(); //... if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; } } } //最后会调用drawSoftware()复制代码
ViewRootImpl.drawSoftware()
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. 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); // The dirty rectangle can be modified by Surface.lockCanvas() //noinspection ConstantConditions 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) { mLayoutRequested = true; // ask wm for a new surface next time. return false; } try { //在画之前清除alpha通道,以便子控件重新组合其图纸上的透明背景 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(); } 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) { // Only clear the flag if it was not set during the mView.draw() call attachInfo.mIgnoreDirtyState = false; } } } finally { try { surface.unlockCanvasAndPost(canvas); } catch (IllegalArgumentException e) { Log.e(mTag, "Could not unlock surface", e); mLayoutRequested = true; // ask wm for a new surface next time. //noinspection ReturnInsideFinallyBlock return false; } if (LOCAL_LOGV) { Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost"); } } return true; }复制代码
最后调用了View.draw()方法
@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; //draw()方法主要流程为: //1、对View的背景进行绘制 //2、保存当前的图层信息 //3、绘制View的内容 //4、对View的子View进行绘制(如果有子View) //5、绘制View的褪色的边缘,类似于阴影效果 //6、绘制View的装饰 // Step 1, draw the background, if needed int saveCount; if (!dirtyOpaque) { //判断View的背景是否是透明的? 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); } }复制代码
画背景:调用了Drawable.draw(canves)方法
private void drawBackground(Canvas canvas) { final Drawable background = mBackground; if (background == null) { return; } //设置背景区域大小 setBackgroundBounds(); // Attempt to use a display list if requested. if (canvas.isHardwareAccelerated() && mAttachInfo != null && mAttachInfo.mThreadedRenderer != 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); } }复制代码
绘制子View
@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 ArrayListpreorderedList = 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 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); //绘制子View } } 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); } }最后调用子View的draw()方法 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); }复制代码