View框架之draw()流程

来源:互联网 发布:geo数据库教程 编辑:程序博客网 时间:2024/05/18 01:22

   在前两篇我们分别描述了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的三大流程已经讲述完毕,谢谢大家的阅读!

0 0
原创粉丝点击