View的工作流程--draw过程

调用流程

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()方法具体实现

注释中写的很清楚,一共分为六步:

  1. 绘制背景
  2. 存储图层(可跳过)
  3. 绘制自身
  4. 绘制子View
  5. 绘制阴影并恢复图层(可跳过)
  6. 绘制滚动条等效果
    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);
        }
    }

如果需要绘制边框阴影

如果要绘制阴影,就不会进入上面的方法块,它的步骤如下:

  1. 绘制背景
  2. 计算阴影带来的变量影响,对阴影分类去保存图层
  3. 绘制自身
  4. 绘制子View
  5. 分类去绘制阴影
  6. 加载保存的图层
  7. 绘制装饰
// 绘制背景
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);

时序图

image

View的setWillNotDraw()

如果一个View不需要绘制任何内容,设置这个标记位为true以后,系统会进行相应的优化。默认情况下,View没有启用这个标记位,但是ViewGroup会默认启用。如果ViewGroup需要通过onDraw绘制内容时,需要显式的关闭WILL_NOT_DRAW这个标记位。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值