View框架之draw()流程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhou12314/article/details/53022945

   在前两篇我们分别描述了View的测量和布局,今天我们就针对绘制的最后一步draw()进行分析。

-

   在开始我们还是先贴张时序图,然后针对图中的方法进行梳理

-

这里写图片描述

  draw()流程相对于测量和布局要简单很多,我们还是从ViewRootimpl中的performDraw()开始分析

  private void performDraw() {
       /** 省略部分代码
        draw(fullRedrawNeeded);
         /** 省略部分代码

}

 private void draw(boolean fullRedrawNeeded) {

/**省略部分代码

            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                return;
            }

}

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
        canvas = mSurface.lockCanvas(dirty);       
        canvas.setDensity(mDensity);
    }

    try {
        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
                    + canvas.getWidth() + ", h=" + canvas.getHeight());
            //canvas.drawARGB(255, 255, 0, 0);
        }

    /**省略部分代码

          //调用DecorView的draw方法
            mView.draw(canvas);


        }
    }
}

接着就执行到了我们DecorVeiw的draw()方法,draw()方法,基本上我们绘制的主要操作都在这里面

下面方法主要做了这几件事情:
            1.绘制背景
            2.绘制View本身
            3.如果本身是ViewGroup的时候,绘制子View
            4.绘制装饰部分,滚动条等等...

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

    // 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
        //绘制View的主要内容,一般子类需要重写此方法
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        //绘制子View,就是调用子View的draw()方法
        dispatchDraw(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);

        // we're done...
        return;
    }

dispatchDraw()本身在View中是一个空实现,真正的实现在ViewGroup中,该方法主要起的作用是循环遍历每一个子View,并执行drawChild();

@Override
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;

                       int clipSaveCount = 0;
    final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
    if (clipToPadding) {
        clipSaveCount = canvas.save();
        canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                mScrollX + mRight - mLeft - mPaddingRight,
                mScrollY + mBottom - mTop - mPaddingBottom);
    }

  /** 省略部分代码

    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) {
                    /////////调用drawChild
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
                transientIndex = -1;
            }
        }


    }

}
-

接着再看一下drawChild()的代码

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

这个draw()也是view里面的方法,被drawChild()方法调用,主要判断是否有绘制缓存,如果有,直接使用缓存,如果没有,重复调用上面的draw()方法

   boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        if (!drawingWithDrawingCache) {
        if (drawingWithRenderNode) {
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
        } else {
            // Fast path for layouts with no backgrounds
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                dispatchDraw(canvas);
            } else {
                draw(canvas);
            }
        }
        }

总结几点:

  1.onDraw()必须在View需要我们自己去实现,绘制内容
   2.dispathDraw()在ViewGroup已经实现好,默认会调用子View的draw()方法
  3.view的绘制的顺序就是我们给一个ViewGroup添加子View的顺序

-
差不多draw()的主要内容就这么多了,到目前为止,view的三大流程已经讲述完毕,谢谢大家的阅读!

猜你喜欢

转载自blog.csdn.net/zhou12314/article/details/53022945