美文网首页Android自定义View
浅谈ondraw的前世今身

浅谈ondraw的前世今身

作者: 暴走的小青春 | 来源:发表于2018-11-18 13:31 被阅读71次

    相信很多人对ondraw都有疑惑,到底他的canvas参数是谁赋值的呢,下面用源码来分析下:
    首先我们知道ondraw在view的源码里是一个空方法,具体还是要view去实现,当然调用者是view的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;
            }
    

    这里贴上draw的源码,我们重点看第4步是dispatchDraw(canvas)
    也就是传统意义上所说的分发,这个方法一般来说是有viewgroup所重写的,包括继承viewGroup的类都不用重写此方法, 这点和viewGroup的onLayout不同,毕竟分发是固定的,确定子view的位置是不固定的,那我们去到dispatchDraw里看下是如何分发的

    @Override
        protected void dispatchDraw(Canvas canvas) {
           。。。
           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;
                }
            }
    
    

    可以看到drawchild里可以看到分发的影子了

     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            return child.draw(canvas, this, drawingTime);
        }
    

    咦,调用的是view三个参数的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) {
            final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
            /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
             *
             *是否支持硬件加速
             */
            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) {
                    // No longer animating: clear out old animation matrix
                    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;
    
            // Sets the flag as early as possible to allow draw() implementations
            // to call invalidate() successfully when doing animations
            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) {
                // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
                // retain the flag's value temporarily in the mRecreateDisplayList flag
                mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
                mPrivateFlags &= ~PFLAG_INVALIDATED;
            }
    
            RenderNode renderNode = null;
            Bitmap cache = null;
            int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
             /*
             *不支持硬件加速的绘制
             */
            if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
                 if (layerType != LAYER_TYPE_NONE) {
                     // If not drawing with RenderNode, treat HW layers as SW
                     layerType = LAYER_TYPE_SOFTWARE;
                     buildDrawingCache(true);
                }
                cache = getDrawingCache(true);
            }
             /*
             *支持硬件加速的绘制
             */
            if (drawingWithRenderNode) {
                // Delay getting the display list until animation-driven alpha values are
                // set up and possibly passed on to the view
                renderNode = updateDisplayListIfDirty();
                if (!renderNode.isValid()) {
                    // Uncommon, but possible. If a view is removed from the hierarchy during the call
                    // to getDisplayList(), the display list will be marked invalid and we should not
                    // try to use it again.
                    renderNode = null;
                    drawingWithRenderNode = false;
                }
            }
    

    我们都知道从3.0开始默认都开启硬件加速的,当然也可以手动关闭,
    从源码中看,不支持的话调用的是buildDrawingCache这方法,里面在view的buildDrawingCacheImpl可以看到分发的代码

    private void buildDrawingCacheImpl(boolean autoScale) {
            。。。
            //scroller常调用的方法
            computeScroll();
            final int restoreCount = canvas.save();
    
            if (autoScale && scalingRequired) {
                final float scale = attachInfo.mApplicationScale;
                canvas.scale(scale, scale);
            }
            //scroller参数为啥是负数的原因
            canvas.translate(-mScrollX, -mScrollY);
    
            mPrivateFlags |= PFLAG_DRAWN;
            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
                    mLayerType != LAYER_TYPE_NONE) {
                mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
            }
    
            draw的代码
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                dispatchDraw(canvas);
                drawAutofilledHighlight(canvas);
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.getOverlayView().draw(canvas);
                }
            } else {
                draw(canvas);
            }
    
            canvas.restoreToCount(restoreCount);
            canvas.setBitmap(null);
    
            if (attachInfo != null) {
                // Restore the cached Canvas for our siblings
                attachInfo.mCanvas = canvas;
            }
        }
    

    这里我们终于看到了viewGroup调用view的draw的代码了,也知道了canvas的构成和bitmap有着关系当然如果他的子view还是viewGroup的话,就会跳过draw,除非有背景,接下来我们看下如今基本所有默认设置的硬件加速的情况,是调用了一个叫view的updateDisplayListIfDirty的方法

    public RenderNode updateDisplayListIfDirty() {
            final RenderNode renderNode = mRenderNode;
            if (!canHaveDisplayList()) {
                // can't populate RenderNode, don't try
                return renderNode;
            }
    
            if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
                    || !renderNode.isValid()
                    || (mRecreateDisplayList)) {
                // Don't need to recreate the display list, just need to tell our
                // children to restore/recreate theirs
                if (renderNode.isValid()
                        && !mRecreateDisplayList) {
                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    dispatchGetDisplayList();
    
                    return renderNode; // no work needed
                }
    
                // If we got here, we're recreating it. Mark it as such to ensure that
                // we copy in child display lists into ours in drawChild()
                mRecreateDisplayList = true;
    
                int width = mRight - mLeft;
                int height = mBottom - mTop;
                int layerType = getLayerType();
    
                final DisplayListCanvas canvas = renderNode.start(width, height);
    
                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;
    
                        // Fast path for layouts with no backgrounds
                        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                            dispatchDraw(canvas);
                            drawAutofilledHighlight(canvas);
                            if (mOverlay != null && !mOverlay.isEmpty()) {
                                mOverlay.getOverlayView().draw(canvas);
                            }
                            if (debugDraw()) {
                                debugDrawFocus(canvas);
                            }
                        } else {
                            draw(canvas);
                        }
                    }
                } finally {
                    renderNode.end(canvas);
                    setDisplayListProperties(renderNode);
                }
            } else {
                mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            }
            return renderNode;
        }
    

    很明显他这里又判断了一遍是否是硬件加速,当然我们也知道了canvas是有renderNode.start(width, height);方法得到的在else里面我们看到了分发的代码.

    好了,基本都看完了,那现在有个问题就是谁第一个调用了draw方法呢,这得从viewRootImpl说起了,所有的分发都是从viewRootImpl的performTraversals方法开始的里面很明显可以看到调用了performDraw的代码

    private void performDraw() {
          if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
              return;
          } else if (mView == null) {
              return;
          }
    
          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 {
              boolean canUseAsync = draw(fullRedrawNeeded);
              if (usingAsyncReport && !canUseAsync) {
                  mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                  usingAsyncReport = false;
              }
          } finally {
              mIsDrawing = false;
              Trace.traceEnd(Trace.TRACE_TAG_VIEW);
          }
    

    而后有调用了draw方法,此时又调用了ThreadedRenderer里的updateRootDisplayList方法

    private void updateRootDisplayList(View view, DrawCallbacks 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.onPreDraw(canvas);
    
                    canvas.insertReorderBarrier();
                    canvas.drawRenderNode(view.updateDisplayListIfDirty());
                    canvas.insertInorderBarrier();
    
                    callbacks.onPostDraw(canvas);
                    canvas.restoreToCount(saveCount);
                    mRootNodeNeedsUpdate = false;
                } finally {
                    mRootNode.end(canvas);
                }
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    

    我们可以看到onPreDraw也是这里执行回掉的,当然,最终也调用了
    updateViewTreeDisplayList这里的view就是decorView,然后又会调用view的updateDisplayListIfDirty就行分发了

     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;
        }
    

    关于onDraw的差不多就说完了,也知道了canvas的由来,当然里面牵扯到的native方法和ThreadedRenderer以及DisplayList渲染过程分析都没有细讲(因为本人也了解的不是很清楚),有兴趣的可以看下老罗的android之旅..

    相关文章

      网友评论

        本文标题:浅谈ondraw的前世今身

        本文链接:https://www.haomeiwen.com/subject/eutgfqtx.html