Android 源码分析三 View 绘制

前文讲完 View 的测量过程,接着讲 View 的绘制。对于 View 绘制,首先想到就是 Canvas 对象以及 draw() onDraw() 相关回调方法。 接下来,也带着一些问题来分析源码:node

  1. Canvas 是啥时候由谁建立?
  2. parent 的 Canvas 和 child 的 Canvas 是同一个对象吗?
  3. 每次 draw() onDraw() 方法中的 Canvas 是同一个对象吗?
  4. 动画效果的实现原理

draw() 方法

View 的绘制,就是从本身的 draw() 方法开始,咱们先从 draw() 方法中看看能找出一些什么线索(measure() layout() draw() 三个方法中,只有draw() 方法能被复写,听说这是一个 bug )。android

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;
    }
    ...
    if (debugDraw()) {
        debugDrawFocus(canvas);
    }
}
复制代码

draw() 方法已经有很详细的注释,详尽的流程分为七个步骤,若是没有渐变遮罩那些效果,一般效果就是绘制背景色(drawBackGround)绘制内容(onDraw()),分发绘制(dispatchDraw()),绘制前景色,绘制高亮,最后,你看到还有一个 debugDraw() 的判断,这个是啥呢?程序员

//View
final private void debugDrawFocus(Canvas canvas) {
    if (isFocused()) {
        final int cornerSquareSize = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
        final int l = mScrollX;
        final int r = l + mRight - mLeft;
        final int t = mScrollY;
        final int b = t + mBottom - mTop;

        final Paint paint = getDebugPaint();
        paint.setColor(DEBUG_CORNERS_COLOR);

        // Draw squares in corners.
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(l, t, l + cornerSquareSize, t + cornerSquareSize, paint);
        canvas.drawRect(r - cornerSquareSize, t, r, t + cornerSquareSize, paint);
        canvas.drawRect(l, b - cornerSquareSize, l + cornerSquareSize, b, paint);
        canvas.drawRect(r - cornerSquareSize, b - cornerSquareSize, r, b, paint);

        // Draw big X across the view.
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawLine(l, t, r, b, paint);
        canvas.drawLine(l, b, r, t, paint);
    }
}
复制代码

其实就是开启 「显示布局边界」以后的那些效果,到这里,意外发现了一个有趣的东西,开启 「显示布局边界」的效果原来就是在 Viewdraw() 方法中指定的。接着重点看看 dispatchDraw() 方法实现。在 View 中,这个方法默认是空实现,由于它就是最终 View,没有 child 须要分发下去。那 ViewGroup 中的实现效果呢?canvas

// ViewGroup dispatchDraw()
 @Override
protected void dispatchDraw(Canvas canvas) {
    ...
    // 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();
    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;
            }
        }
        // 获取 childIndex 
        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);
        }
    }
    ...
}
复制代码

ViewGroup.dispatchDraw() 方法中,最核心逻辑就是遍历全部的 child , 而后调用 drawChild() 方法,固然,调用 drawChild() 也有一些条件,好比说 View 是可见的。再说 drawChild() 方法以前,咱们能够先看到,这里有一个方法来获取 childIndex ,既然有一个方法,就说明,childIndex 或者说 child 的绘制 index 是能够改变的咯?缓存

private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
    final int childIndex;
    if (customOrder) {
        if (childIndex1 >= childrenCount) {
            throw new IndexOutOfBoundsException("getChildDrawingOrder() "
                    + "returned invalid index " + childIndex1
                    + " (child count is " + childrenCount + ")");
        }
        childIndex = childIndex1;
    } else {
        childIndex = i;
    }
    return childIndex;
}
复制代码

getAndVerifyPreorderedIndex() 接收三个参数,第一个是 totalCount,第二个在 parent 中的位置,第三个 customOrder 是说是否支持自定义顺序,默认是 false ,能够经过 setChildrenDrawingOrderEnabled() 方法更改。若是咱们支持自定义绘制顺序以后,具体绘制顺序就会根据 getChildDrawingOrder() 方法返回,可能你会想了,为何须要修改绘制顺序呢?有必要吗?那妥妥是有必要的,绘制顺序决定了显示层级。好了,这又算一个额外发现,关于修改 View 绘制顺序。app

接着看看调用的 View.draw() 三个参数的重载方法,好戏开始啦。dom

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.
     *
     * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
     * HW accelerated, it can't handle drawing RenderNodes.
     */
    boolean drawingWithRenderNode = mAttachInfo != null
            && mAttachInfo.mHardwareAccelerated
            && hardwareAcceleratedCanvas;

    boolean more = false;
    final boolean childHasIdentityMatrix = hasIdentityMatrix();
    final int parentFlags = parent.mGroupFlags;

    ...

    if (hardwareAcceleratedCanvas) {
        // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
        // retain the flag's value temporarily in the mRecreateDisplayList flag
        // INVALIDATED 这个 flag 被重置,可是它的值被保存到 mRecreateDisplayList 中,后面绘制时须要使用
        mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
        mPrivateFlags &= ~PFLAG_INVALIDATED;
    }

    RenderNode renderNode = null;
    ...
    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;
        }
    }
    ...
    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);
            }
        }
    } 
    ...
    // 重置 mRecreateDisplayList 为 false 返回是否还有更多 这个和动画绘制有关系
    mRecreateDisplayList = false;
    return more;
}
复制代码

在这个方法中,首先要注意的是,canvas.isHardwareAccelerated() 第一行代码这个判断,Canvas 不就是单纯的 Canvas,里面还有支持硬件加速不支持硬件加速的区分?先看一哈 Canvas 的种族关系。CanvasBaseCanvas 的一个实现类,它 isHardwareAccelerated 方法是返回的 false,那就是说,确定还有其余子类咯,果真 RecordingCanvas 、而后还有 DisplayListCanvas ,而后 DisplayListCanvas 这个类中 isHardwareAccelerated() 返回的就是 true 。ide

到这里,虽然还没看到 Canvas 在哪里建立出来,可是至少首先明确了 Canvas 是有细分子类,并且支持硬件加速的不是 Canvas 这个类,而是 DisplayListCanvas 。如今硬件加速默认都是支持的,那咱们能够先验证一下 Canvas 的类型。随便定义两个 View ,而后写一个布局打印以下:源码分析

TestFrameLayout draw canvas:android.view.DisplayListCanvas@6419f23
TestFrameLayout dispatchDraw canvas:android.view.DisplayListCanvas@6419f23
TestView draw canvas:android.view.DisplayListCanvas@7f9c20
TestView onDraw canvas:android.view.DisplayListCanvas@7f9c20
TestView dispatchDraw dispatchDraw:android.view.DisplayListCanvas@7f9c20
TestView draw canvas:android.view.DisplayListCanvas@369cf9b
TestView onDraw canvas:android.view.DisplayListCanvas@369cf9b
TestView dispatchDraw dispatchDraw:android.view.DisplayListCanvas@369cf9b
复制代码

首先,Canvas 的确是 DisplayListCanvas 的类型。而后,两次 draw() 方法,是两个不一样的 Canvas 对象。最后,parent 和 child 用的不是同一个对象,彷佛以前提的问题基本上都在这个 log 中所有给出答案。答案知道没啥用,咱们是看源码分析具体怎么操做的。因此,仍是继续看下去。布局

接下来,咱们就先看支持硬件加速这条分支,这也是咱们的常规路线。

View 之 flag

在上面的方法中,若是支持硬件加速后,就有这一步骤。这里涉及到 View中 Flag 操做。View 中有超级多状态,若是每个都用一个变量来记录,那就是一个灾难。那么怎么能用最小的花销记录最多的状态呢?这个就和前文讲到 测量模式和测量大小用一个字段的高位和低位就搞定同样。二进制这个时候就很是高效啦,看看 View 中定义了哪些基础 flag 。

// for mPrivateFlags:
    static final int PFLAG_WANTS_FOCUS                 = 0x00000001;
    static final int PFLAG_FOCUSED                     = 0x00000002;
    static final int PFLAG_SELECTED                    = 0x00000004;
    static final int PFLAG_IS_ROOT_NAMESPACE           = 0x00000008;
    static final int PFLAG_HAS_BOUNDS                  = 0x00000010;
    static final int PFLAG_DRAWN                       = 0x00000020;
    static final int PFLAG_DRAW_ANIMATION              = 0x00000040;
    static final int PFLAG_SKIP_DRAW                   = 0x00000080;
    static final int PFLAG_REQUEST_TRANSPARENT_REGIONS = 0x00000200;
    static final int PFLAG_DRAWABLE_STATE_DIRTY        = 0x00000400;
    static final int PFLAG_MEASURED_DIMENSION_SET      = 0x00000800;
    static final int PFLAG_FORCE_LAYOUT                = 0x00001000;
    static final int PFLAG_LAYOUT_REQUIRED             = 0x00002000;
    private static final int PFLAG_PRESSED             = 0x00004000;
    static final int PFLAG_DRAWING_CACHE_VALID         = 0x00008000;
复制代码

定义是定义好了,那么怎么修改状态呢?这里就用到位运算 与 或 非 异或 等操做符号。

// 判断是否包含 一个 flag (同 1 为 1 else 0)
view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0 
// 清除一个 flag  
view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT
// 设置一个 flag  (遇 1 为 1 else 0)
view.mPrivateFlags |= View.PFLAG_FORCE_LAYOUT

//检查是否改变
int old = mViewFlags;
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
// changed ==1 为 true 
int changed = mViewFlags ^ old;


// View.setFlag()
if ((changed & DRAW_MASK) != 0) {
    if ((mViewFlags & WILL_NOT_DRAW) != 0) {
        if (mBackground != null
                || mDefaultFocusHighlight != null
                || (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
            mPrivateFlags &= ~PFLAG_SKIP_DRAW;
        } else {
            mPrivateFlags |= PFLAG_SKIP_DRAW;
        }
    } else {
        mPrivateFlags &= ~PFLAG_SKIP_DRAW;
    }
}
复制代码

接着在上面方法中,就开始对 flag 进行操做。

if (hardwareAcceleratedCanvas) {
        // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
        // retain the flag's value temporarily in the mRecreateDisplayList flag
        // INVALIDATED 这个 flag 被重置,可是它的值被保存到 mRecreateDisplayList 中,后面绘制时须要使用
        mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
        mPrivateFlags &= ~PFLAG_INVALIDATED;
    }
复制代码

首先将 mRecreateDisplayList 赋值为是否包含 PFLAG_INVALIDATED 的状态。而后重置 PFLAG_INVALIDATED flag。紧接着就调用 updateDisplayListIfDirty() 方法,接下来重点看下 updateDisplayListIfDirty() 方法中的逻辑。

// View
@NonNull
public RenderNode updateDisplayListIfDirty() {
    final RenderNode renderNode = mRenderNode;
    if (!canHaveDisplayList()) {
        // can't populate RenderNode, don't try
        return renderNode;
    }
    // 1.没有 PFLAG_DRAWING_CACHE_VALID 或者 renderNode 不可用 或者 mRecreateDisplayList 为 true (含有 PFLAG_INVALIDATED )
    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
        // 2.这里 mRecreateDisplayList 在 前面的 draw(Canvas canvas, ViewGroup parent, long drawingTime) 中肯定
        // 设置过 PFLAG_INVALIDATED 才会返回 true 须要从新建立 canvas 并绘制 
        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();
        // 3.这里,经过 renderNode 建立出了 DisplayListCanvas
        final DisplayListCanvas canvas = renderNode.start(width, height);
        canvas.setHighContrastText(mAttachInfo.mHighContrastText);

        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
                // 4.ViewGroup 不用绘制内容
                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 {
            // 5.一些收尾工做
            renderNode.end(canvas);
            setDisplayListProperties(renderNode);
        }
    } else {
        // 异常状况一 相关标志添加和清除
        mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    }
    return renderNode;
}
复制代码

updateDisplayIfDirty() 方法中,这些标志必定要注意:

mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
复制代码

再绘制前,这三个 flag 的改变是必定要执行的,具体说就是 PFLAG_DRAWN PFLAG_DRAWING_CACHE_VALID 被添加,PFLAG_DIRTY_MASK 被清除。这里就再添加一个疑问,PFLAG_DRAWN PFLAG_DRAWING_CACHE_VALID 何时被移除的?PFLAG_DIRTY_MASK 何时被添加的?这个放后面说。先直接来分析一波代码执行。

接着看相关源码,在注释1的地方,三个条件。

(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
            || !renderNode.isValid()
            || (mRecreateDisplayList)
复制代码

已目前的状况,咱们就知道第三个字段是上一个方法清除 PFLAG_INVALIDATED 时保存的它的状态。咱们姑且认为它就是 true ,那接着注释2中的添加就不知足,接着就到 注释3中,这里很清楚能够看到,Canvas 在这里被建立出来啦。第一个问题终于找到答案,CanvasView 本身在 updateDiasplayIfDirty() 方法中建立出来的。建立 Canvas 以后,若是是硬解模式下,就到注释4中,这里是一个判断,若是有 PFLAG_SKIP_DRAW 这个 flag,直接就调用 dispatchDraw() 分发下去,不然就调用本身的 draw() 方法回到文章开始说的 draw() 方法中。

那么这个 PFLAG_SKIP_DRAW 又是哪里会有设置呢?在 ViewGroup 的构造方法中,我看到了这个:

private void initViewGroup() {
    // ViewGroup doesn't draw by default
    if (!debugDraw()) {
        setFlags(WILL_NOT_DRAW, DRAW_MASK);
    }
   ...
}
复制代码

若是没有开启「显示边界布局」,直接会添加 WILL_NOT_DRAW 的 flag。这里就是一个对于 ViewGroup 的优化,由于 ViewGroup 绘制 content (调用 onDraw())方法有时候是多余的,它的内容明显是由 child 本身完成。可是,若是我给 ViewGroup 设置了背景,文章开头 draw() 方法分析中就有说,先绘制背景色,那若是这个时候跳过 ViewGroupdraw() 直接调用 dispatchDraw() 方法确定有问题,或者说在设置背景色相关方法中,View 又会修改这个 flag。

public void setBackgroundDrawable(Drawable background) {
    ...

    if (background != null) {
        ...
        applyBackgroundTint();

        // Set callback last, since the view may still be initializing.
        background.setCallback(this);
        // 清除 PFLAG_SKIP_DRAW 
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
            mPrivateFlags &= ~PFLAG_SKIP_DRAW;
            requestLayout = true;
        }
    } else {
        /* Remove the background */
        mBackground = null;
        if ((mViewFlags & WILL_NOT_DRAW) != 0
                && (mDefaultFocusHighlight == null)
                && (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
            // 若是没有背景,就再次添加 PFLAG_SKIP_DRAW
            mPrivateFlags |= PFLAG_SKIP_DRAW;
        }
        requestLayout = true;
    }

    computeOpaqueFlags();

    if (requestLayout) {
        // 请求从新布局
        requestLayout();
    }

    mBackgroundSizeChanged = true;
    // 要求从新绘制
    invalidate(true);
    invalidateOutline();
}
复制代码

注意,更新背景以后会触发 requestLayout()invalidate() 两个方法。

那若是三个条件都不知足(异常状况一),就是直接更改 flag 结束了;还有就是注释2中的一种状况(异常状况二),mRecreateDisplayList 为 false,不会再去建立 Canvas ,也就是说它不须要从新绘制本身,可是会调用 dispatchGetDisplayList() 方法。这个方法在 View 中是空实现,在 ViewGroup 中会遍历 child 调用 recreateChildDisplayList(child) 方法。

private void recreateChildDisplayList(View child) {
    child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
    child.mPrivateFlags &= ~PFLAG_INVALIDATED;
    child.updateDisplayListIfDirty();
    child.mRecreateDisplayList = false;
}
复制代码

这个方法像极了 View.draw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中的核心设置。做用就是在不绘制本身的状况下,将绘制再次进行分发。这两种状况何时触发?第一种不太好猜,第二种其实很好理解,那就是当咱们调用 invalidate() 调用以后,确定就只更新对应的 View,不可能说所有都去从新绘制,这样太浪费资源和作无用功。具体的下面作分析。

Canvas 建立和复用

在上面 updateDisplayListIfDirty() 方法中,咱们解决了第一个问题,Canvas 是在这个方法中建立:

// 3.这里,经过 renderNode 建立出了 DisplayListCanvas
final DisplayListCanvas canvas = renderNode.start(width, height);
canvas.setHighContrastText(mAttachInfo.mHighContrastText);
复制代码

接下来看看 Canvas 具体建立过程。首先是 renderNode 这个对象。在 View 的构造方法中,

mRenderNode = RenderNode.create(getClass().getName(), this);
复制代码

内部就是调用 native 相关方法,传入对应 class 名称和所属对象。接着再看看 renderNode 建立 Canvas 的过程。

//DisplayListCanvas
static DisplayListCanvas obtain(@NonNull RenderNode node, int width, int height) {
    if (node == null) throw new IllegalArgumentException("node cannot be null");
    // acquire  取出最后一个
    DisplayListCanvas canvas = sPool.acquire();
    if (canvas == null) {
        canvas = new DisplayListCanvas(node, width, height);
    } else {
        nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,
                width, height);
    }
    canvas.mNode = node;
    canvas.mWidth = width;
    canvas.mHeight = height;
    return canvas;
}
复制代码

Canvas 的管理用到 pool 的概念,经过一个池来实现回收(release)复用(acquire) ,具体怎么回收复用的,下面有贴对应源码。最后在 finally 中,会对 Canvas 进行释放。 这里 pool 并无初始 size,或者说初始 size 就是 0 ,最大 size 是在 DisplayListCanvas 中指定为 POOL_LIMIT = 25DisplayListCanvas 还额外指定了 drawBitmap() 方法中 bitmap 最大的 size 100M。

//RenderNode 
public void end(DisplayListCanvas canvas) {
    long displayList = canvas.finishRecording();
    nSetDisplayList(mNativeRenderNode, displayList);
    canvas.recycle();
}

//DisplayListCanvas
void recycle() {
    mNode = null;
    // 存入最后一个
    sPool.release(this);
}

//SimplePool
@Override
@SuppressWarnings("unchecked")
public T acquire() {
    if (mPoolSize > 0) {
        final int lastPooledIndex = mPoolSize - 1;
        T instance = (T) mPool[lastPooledIndex];
        mPool[lastPooledIndex] = null;
        mPoolSize--;
        return instance;
    }
    return null;
}
//SimplePool
@Override
public boolean release(@NonNull T instance) {
    if (isInPool(instance)) {
        throw new IllegalStateException("Already in the pool!");
    }
    if (mPoolSize < mPool.length) {
        mPool[mPoolSize] = instance;
        mPoolSize++;
        return true;
    }
    return false;
}
复制代码

这里也具体说明上文提出的问题,每一次绘制,View 都会使用一个新的 Canvas(从pool中取出来),不排除是以前已经使用过的。使用完毕,回收又放回 pool。ViewGroupchild 之间不会同时使用同一个 Canvas ,可是能共享一个 pool 中的资源。

View draw.png

invalidate()

好了,上面捋清楚 View 绘制的整个过程后,提出的问题也解决的差很少了,可是还遗留了 updateListPlayIfDirty() 方法中两个异常状况。若是三个条件都不知足(异常状况一),就直接更改 flag 结束;还有就是注释2中的一种状况(异常状况二),mRecreateDisplayList 为false,不会再去建立 Canvas ,也就是说它不须要从新绘制本身。

接着,把这两个异常状况解决就圆满结束。要解决上面两个异常问题,咱们就必须来分析一波主动调用 invalidate() 请求绘制。

在调用 invalidate() 方法以后,最后会调用到 invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) 方法,并且 invalidateCache fullInvalidate 都为 true

//View
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
        boolean fullInvalidate) {
    ...

    if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
            || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
            || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
            || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
        // fullInvalidate 时 clear PFLAG_DRAWN 
        if (fullInvalidate) {
            mLastIsOpaque = isOpaque();
            mPrivateFlags &= ~PFLAG_DRAWN;
        }
        // 调用 draw 等的通行证,
        mPrivateFlags |= PFLAG_DIRTY;

        if (invalidateCache) {
            mPrivateFlags |= PFLAG_INVALIDATED;
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        }

        // Propagate the damage rectangle to the parent view.
        final AttachInfo ai = mAttachInfo;
        final ViewParent p = mParent;
        if (p != null && ai != null && l < r && t < b) {
            final Rect damage = ai.mTmpInvalRect;
            damage.set(l, t, r, b);
            p.invalidateChild(this, damage);
        }

        // Damage the entire projection receiver, if necessary.
        if (mBackground != null && mBackground.isProjected()) {
            final View receiver = getProjectionReceiver();
            if (receiver != null) {
                receiver.damageInParent();
            }
        }
    }
}
复制代码

invalidateInternal() 方法中,一开始提到的那些 flag 又出现了。其中 PFLAG_DRAWNPFLAG_DRAWING_CACHE_VALID 被清除掉 PFLAG_DIRTYPFLAG_INVALIDATED 被添加。这里这些 flag 请注意,这是解答那两个异常状况的核心。接着会调用到 invalidateChild() 方法。

//ViewGroup
@Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null && attachInfo.mHardwareAccelerated) {
        // HW accelerated fast path
        onDescendantInvalidated(child, child);
        return;
    }

    ViewParent parent = this;
    if (attachInfo != null) {
       ...
        do {
            ...
            // 依次返回 parent 的 parent 最后到 ViewRootImpl
            parent = parent.invalidateChildInParent(location, dirty);
            if (view != null) {
                // Account for transform on current parent
                Matrix m = view.getMatrix();
                if (!m.isIdentity()) {
                    RectF boundingRect = attachInfo.mTmpTransformRect;
                    boundingRect.set(dirty);
                    m.mapRect(boundingRect);
                    dirty.set((int) Math.floor(boundingRect.left),
                            (int) Math.floor(boundingRect.top),
                            (int) Math.ceil(boundingRect.right),
                            (int) Math.ceil(boundingRect.bottom));
                }
            }
        } while (parent != null);
    }
}

这个方法中,若是是硬解支持,直接走 `onDescendantInvalidated(child, child)` 方法。接着看看这个方法的具体实现。

@Override
@CallSuper
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
    /*
     * HW-only, Rect-ignoring damage codepath
     *
     * We don't deal with rectangles here, since RenderThread native code computes damage for
     * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area)
     */

    // if set, combine the animation flag into the parent
    mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);

    // target 须要被从新绘制时,至少有 invalidate flag 
    if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
        // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
        // optimization in provides in a DisplayList world.
        // 先清除全部 DIRTY 相关 flag 而后 加上 DIRTY flag 
        mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;

        // simplified invalidateChildInParent behavior: clear cache validity to be safe...
        // 清除 PFLAG_DRAWING_CACHE_VALID 标志
        mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
    }

    // ... and mark inval if in software layer that needs to repaint (hw handled in native)
    if (mLayerType == LAYER_TYPE_SOFTWARE) {
        // Layered parents should be invalidated. Escalate to a full invalidate (and note that
        // we do this after consuming any relevant flags from the originating descendant)
        mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
        target = this;
    }

    if (mParent != null) {
        mParent.onDescendantInvalidated(this, target);
    }
}

这个方法中,会依次向上让 parent 调用 `onDescendantInvalidated()` ,而在这个方法中,会为 parent 添加 `PFLAG_DIRTY` 和 重置 `PFLAG_DRAWING_CACHE_VALID` 标志,可是,可是,请注意这里没有给 parent 设置过 `PFLAG_INVALIDATED` ,由于除了发起 `invalidate()` 的 targetView ,其余 `View` 理论上不用从新绘制。

ViewTree 的尽头是啥呢?是 `ViewRootImpl` ,这里就不详细展开说了,在那里,最后会调用 `performTraversals()` 方法,在该方法中,最后会调用 `performDraw()` 方法,在这个方法中,最后又会调用 `mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this)`,而该方法最后会调用到 `updateViewTreeDisplayList()` 方法。

//ViewRootImpl
//ThreadedRenderer
private void updateViewTreeDisplayList(View view) {
    view.mPrivateFlags |= View.PFLAG_DRAWN;
    // 调用 invalidate 到这里时,除了 targetView 其余 View 都未设置过 PFLAG_INVALIDATED mRecreateDisplayList 为 false
    view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
            == View.PFLAG_INVALIDATED;
    view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    view.updateDisplayListIfDirty();
    view.mRecreateDisplayList = false;
}
复制代码

这个方法和上面介绍过的 ViewGroup.recreateChildDisplayList(View child) 很类似,就是多了 PFLAG_DRAWN 设置。到这里,就开始整个 View 绘制的分发啦。调用上文提到的 updateDisplayListIfDirty() 方法。 再来看这个异常状况一:

(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
            || !renderNode.isValid()
            || (mRecreateDisplayList))
复制代码

调用 invalidate() 后在 onDescendantInvalidated() 中, PFLAG_DRAWING_CACHE_VALID 都被清除掉了。因此不会走到异常状况一中。接着,看异常状况二,mRecreateDisplayList 为 false ,这个就符合了,在 mRecreateDisplayList() 方法向上传递过程当中,并无给 targetView 之外的 View 设置过 PFLAG_INVALIDATED ,因此异常状况二就是咱们调用 invalidate() 主动要求绘制时会执行。

那异常状况一到底怎么触发呢?经过上面分析能够知道,每一次绘制结束,PFLAG_DRAWING_CACHE_VALID 都会被添加。每一次开始绘制,PFLAG_DRAWING_CACHE_VALID 又会被清除。当一个 View 知足没有设置 PFLAG_INVALIDATED 而且 PFLAG_DRAWING_CACHE_VALID 又没有被清除(至少说没有触发 invalidate())。

public void requestLayout() {
    ...

    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    ...
}
复制代码

当一个 View 调用 requestLayout() 以后,PFLAG_FORCE_LAYOUTPFLAG_INVALIDATED 都会被添加。可是,通常来讲,requestLayout() 不会触发 draw() 方法的,奥妙就在这里。当 requestLayout() 调用到 ViewRootImpl 中以后,又一次执行 performTraversals() 时,完成测量等逻辑以后,再到上文提到的 updateViewTreeDisplayList() 方法时,PFLAG_INVALIDATED 并无被设置,所以 mRecreateDisplayList 为 false,此时只有 targetView 才有设置 PFLAG_INVALIDATED 。而后 PFLAG_DRAWING_CACHE_VALID 默认就被设置,并无被清除。因此,在 RootView.updateDisplayListIfDirty() 执行时,RootView 直接就走到了异常状况一。这也是 requestLayout() 不会回调 draw() 方法的缘由。

可是 requestLayout() 不触发 draw() 不是绝对的。若是你的 size 发生改变,在 layout() 方法中,最后会调用 setFrame() 方法,在该方法中,若是 size change,它会本身调用 invalidate(sizeChange)

protected boolean setFrame(int left, int top, int right, int bottom) {
    boolean changed = false;

    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
        changed = true;

        // Remember our drawn bit
        int drawn = mPrivateFlags & PFLAG_DRAWN;

        int oldWidth = mRight - mLeft;
        int oldHeight = mBottom - mTop;
        int newWidth = right - left;
        int newHeight = bottom - top;
        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

        // Invalidate our old position
        invalidate(sizeChanged);
        ...

        notifySubtreeAccessibilityStateChangedIfNeeded();
    }
    return changed;
}
复制代码

动画相关

这里咱们看看 AnimationAnimator 的区别,效果上说就是 Animation 不会改变一个 View 的真实值,动画结束后又还原(固然,你能够设置 fillAfter 为 true ,可是它的布局仍是在初始位置,只是更改了绘制出来的效果)。 Animator 会直接改变一个 View 的相关属性,结束后不会还原。

View invalidate.png

Animation

@Override
protected void dispatchDraw(Canvas canvas) {
    ...
    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());
        }
    }
    // 动画完成 
    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 
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    ...
        if (a != null && !more) {
        if (!hardwareAcceleratedCanvas && !a.getFillAfter()) {
            onSetAlpha(255);
        }
        //完成相关回调重置动画
        parent.finishAnimatingView(this, a);
    }
    ...
}


//View
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
        Animation a, boolean scalingRequired) {
    Transformation invalidationTransform;
    final int flags = parent.mGroupFlags;
    final boolean initialized = a.isInitialized();
    if (!initialized) {
        a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
        a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
        if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
        // 本身增长 PFLAG_ANIMATION_STARTED
        onAnimationStart();
    }

    final Transformation t = parent.getChildTransformation();
    boolean more = a.getTransformation(drawingTime, t, 1f);
    if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
        if (parent.mInvalidationTransformation == null) {
            parent.mInvalidationTransformation = new Transformation();
        }
        invalidationTransform = parent.mInvalidationTransformation;
        a.getTransformation(drawingTime, invalidationTransform, 1f);
    } else {
        invalidationTransform = t;
    }
    // 动画没有结束
    if (more) {
        // 不会改变界限
        if (!a.willChangeBounds()) {
            if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
                    ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                // 设置了 layoutAnimation 会到这里
                parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
            } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                //通常状况到这里,调用 parent.invalidate() 从新绘制
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                parent.invalidate(mLeft, mTop, mRight, mBottom);
            }
        } else {
            //改变 界限
            if (parent.mInvalidateRegion == null) {
                parent.mInvalidateRegion = new RectF();
            }
            final RectF region = parent.mInvalidateRegion;
            a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                    invalidationTransform);

            // The child need to draw an animation, potentially offscreen, so
            // make sure we do not cancel invalidate requests
            parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

            final int left = mLeft + (int) region.left;
            final int top = mTop + (int) region.top;
            parent.invalidate(left, top, left + (int) (region.width() + .5f),
                    top + (int) (region.height() + .5f));
        }
    }
    return more;
}
复制代码

dispatchDraw() 中最后调用 applyLegacyAnimation() 方法,在这方法中,若是是首次初始化,会增长 PFLAG_ANIMATION_STARTED 标志,接着根据 getTransformation() 返回动画是否没有结束。若是没有结束,就添加相关 flag ,使用 parent.invalidate(mLeft, mTop, mRight, mBottom) 完成对特定区域绘制的更新。

Animator

对于 Animator ,最简单的写法就是:

view.animate()
    .scaleX(0.5f)
    .scaleY(0.5f)
    .start()

private void animatePropertyBy(int constantName, float startValue, float byValue) {
    // First, cancel any existing animations on this property
    if (mAnimatorMap.size() > 0) {
        Animator animatorToCancel = null;
        Set<Animator> animatorSet = mAnimatorMap.keySet();
        for (Animator runningAnim : animatorSet) {
            PropertyBundle bundle = mAnimatorMap.get(runningAnim);
            if (bundle.cancel(constantName)) {
                // property was canceled - cancel the animation if it's now empty
                // Note that it's safe to break out here because every new animation
                // on a property will cancel a previous animation on that property, so
                // there can only ever be one such animation running.
                if (bundle.mPropertyMask == NONE) {
                    // the animation is no longer changing anything - cancel it
                    animatorToCancel = runningAnim;
                    break;
                }
            }
        }
        if (animatorToCancel != null) {
            animatorToCancel.cancel();
        }
    }

    NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
    mPendingAnimations.add(nameValuePair);
    mView.removeCallbacks(mAnimationStarter);
    mView.postOnAnimation(mAnimationStarter);
}
复制代码

animatePropertyBy() 内部注释很清楚,每个属性的动画效果只有一个有效,最新的会将上一个取消掉,在该方法最后,你会看到它直接有开始执行动画效果,等等,咱们这里还咩有调用 start() 呢? 这意思就是说咱们若是须要马上执行,压根儿不用手动调用 start() 方法?答案就是这样的,咱们彻底不用手动调用 start() 去确认开启动画。

private void startAnimation() {
    if (mRTBackend != null && mRTBackend.startAnimation(this)) {
        return;
    }
    ...
    animator.addUpdateListener(mAnimatorEventListener);
    animator.addListener(mAnimatorEventListener);
    ...
    animator.start();
}
复制代码

这里须要注意一下这个 mAnimatorEventListener ,它实现了 Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener 两个接口。在 onAnimationUpdate() 方法中:

// AnimatorEventListener
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        PropertyBundle propertyBundle = mAnimatorMap.get(animation);
        if (propertyBundle == null) {
            // Shouldn't happen, but just to play it safe
            return;
        }

        boolean hardwareAccelerated = mView.isHardwareAccelerated();

        // alpha requires slightly different treatment than the other (transform) properties.
        // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
        // logic is dependent on how the view handles an internal call to onSetAlpha().
        // We track what kinds of properties are set, and how alpha is handled when it is
        // set, and perform the invalidation steps appropriately.
        boolean alphaHandled = false;
        //若是不支持硬件加速,那么将从新出发 draw() 方法
        if (!hardwareAccelerated) {
            mView.invalidateParentCaches();
        }
        float fraction = animation.getAnimatedFraction();
        int propertyMask = propertyBundle.mPropertyMask;
        if ((propertyMask & TRANSFORM_MASK) != 0) {
            mView.invalidateViewProperty(hardwareAccelerated, false);
        }
        ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
        if (valueList != null) {
            int count = valueList.size();
            for (int i = 0; i < count; ++i) {
                NameValuesHolder values = valueList.get(i);
                float value = values.mFromValue + fraction * values.mDeltaValue;
                // alpha 的 设置被区分开
                if (values.mNameConstant == ALPHA) {
                    // 最终调用 view.onSetAlpha() 方法,默认返回为 false
                    alphaHandled = mView.setAlphaNoInvalidation(value);
                } else {
                    // 属性动画修改属性的核心方法
                    setValue(values.mNameConstant, value);
                }
            }
        }
        if ((propertyMask & TRANSFORM_MASK) != 0) {
            if (!hardwareAccelerated) {
                // 不支持硬件加速,手动添加 PFLAG_DRAWN 标志
                mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
            }
        }
        // invalidate(false) in all cases except if alphaHandled gets set to true
        // via the call to setAlphaNoInvalidation(), above
        // 一般都是 false 不会触发 invalidate
        if (alphaHandled) {
            mView.invalidate(true);
        } else {
            // alphaHandled false 的话 不管 硬解仍是软解都会调用该方法
            mView.invalidateViewProperty(false, false);
        }
        if (mUpdateListener != null) {
            mUpdateListener.onAnimationUpdate(animation);
        }
    }

// View.invalidateParentCaches()
protected void invalidateParentCaches() {
    if (mParent instanceof View) {
        ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
    }
}

// View alpha 单独设置
boolean setAlphaNoInvalidation(float alpha) {
    ensureTransformationInfo();
    if (mTransformationInfo.mAlpha != alpha) {
        mTransformationInfo.mAlpha = alpha;
        boolean subclassHandlesAlpha = onSetAlpha((int) (alpha * 255));
        if (subclassHandlesAlpha) {
            mPrivateFlags |= PFLAG_ALPHA_SET;
            return true;
        } else {
            mPrivateFlags &= ~PFLAG_ALPHA_SET;
            mRenderNode.setAlpha(getFinalAlpha());
        }
    }
    return false;
}
复制代码

能够看到,在 animator 内部设置的 AnimatorEventListener 对象中,回调 onAnimationUpdate() 方法核心是经过 setValue(values.mNameConstant, value) 方法改变相关属性。

private void setValue(int propertyConstant, float value) {
    final View.TransformationInfo info = mView.mTransformationInfo;
    final RenderNode renderNode = mView.mRenderNode;
    switch (propertyConstant) {
        case TRANSLATION_X:
            renderNode.setTranslationX(value);
            break;
        ...
        case Y:
            renderNode.setTranslationY(value - mView.mTop);
            break;
        ...
        case ALPHA:
            info.mAlpha = value;
            renderNode.setAlpha(value);
            break;
    }
}
复制代码

能够看到,属性动画的本质是直接修改 renderNode 的相关属性,包括 alpha ,虽然 alpha 并无没有直接调用 setValue() 的方法更改,但本质都是调用到 renderNode 的相关方法。可是,在 Animator 实际执行过程当中,又是区分了 软解和硬解两种状况。

若是是硬解的话,直接修改 renderNode 相关属性,DisplayListCanvas 是关联了 renderNode ,虽然都调用了 invalidateViewProperty() 。 若是是软解的话,首先调用 mView.invalidateParentCaches() 为 parent 添加 PFLAG_INVALIDATED 标志,若是存在 transform ,就为本身再添加 PFLAG_DRAWN。 接着在 mView.invalidateViewProperty(false, false) 中,开始和硬解有了区别。

// View.invalidateViewProperty()
void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
    // 软解 直接 走 invalidate(false) 方法
    if (!isHardwareAccelerated()
            || !mRenderNode.isValid()
            || (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
        if (invalidateParent) {
            invalidateParentCaches();
        }
        if (forceRedraw) {
            // 强制刷新 也是添加 PFLAG_DRAWN
            mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation
        }
        invalidate(false);
    } else {
        // 硬解 走 damageInParent() 方法
        damageInParent();
    }
}
复制代码

在硬解中,直接调用 damageInParent() ,由于这个时候,PFLAG_INVALIDATED 并无设置。在最后的 updateDisplayListIfDirty() 方法中,不会触发 draw() 或者 dispatchDraw() ,流程结束。

而后软解,走 invalidate(false) 使用 false 的话,PFLAG_INVALIDATED 不会被添加,PFLAG_DRAWING_CACHE_VALID 不会被清除, 最后调用 ViewGroup.invalidateChild() 方法,这个方法以前只分析过 硬解 的状况。

@Override
public final void invalidateChild(View child, final Rect dirty) {
    ...
        // 软解
        do {
            ...
            parent = parent.invalidateChildInParent(location, dirty);
            ...
        } while (parent != null);
    }
}

@Override
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
    if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
        // either DRAWN, or DRAWING_CACHE_VALID
        if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
                != FLAG_OPTIMIZE_INVALIDATE) {
           ...
        } else {

           ...
            location[CHILD_LEFT_INDEX] = mLeft;
            location[CHILD_TOP_INDEX] = mTop;

            mPrivateFlags &= ~PFLAG_DRAWN;
        }
        // 这里将 PFLAG_DRAWING_CACHE_VALID 标志清除,这个很重要
        mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        if (mLayerType != LAYER_TYPE_NONE) {
            mPrivateFlags |= PFLAG_INVALIDATED;
        }

        return mParent;
    }

    return null;
}
复制代码

经过上面连个方法,最终会调用到 ViewRootImpl 中开始从新分发,过程和上面分析一致,须要注意的是在 invalidateChildInParent() 方法中 PFLAG_DRAWING_CACHE_VALID 被清除,PFLAG_INVALIDATED 被添加。因此在最后调用 updateDisplayListIfDirty() 方法中不会走到上面提到的两种异常状况中。

@NonNull
public RenderNode updateDisplayListIfDirty() {
    ...
    if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
            || !renderNode.isValid()
            || (mRecreateDisplayList)) {
        try {
            // 使用软解最终调用到这里
            if (layerType == LAYER_TYPE_SOFTWARE) {
                buildDrawingCache(true);
                Bitmap cache = getDrawingCache(true);
                if (cache != null) {
                    canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                }
            } else {
                ...
            }
        } finally {
            renderNode.end(canvas);
            setDisplayListProperties(renderNode);
        }
    } else {
        mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    }
    return renderNode;
}
复制代码

能够看到,使用软解,并不会按以前的硬解分析的走到 dispatchDraw() 或者 draw() 方法,而是调用 buildDrawingCache(boolean autoScale) 方法,在该方法中,最后又会调用 buildDrawingCacheImpl(autoScale) 方法。

private void buildDrawingCacheImpl(boolean autoScale) {
    ...

    Canvas canvas;
    if (attachInfo != null) {
        //从 attachInfo 总获取 Canvas ,没有就建立并存入 attachInfo 中
        canvas = attachInfo.mCanvas;
        if (canvas == null) {
            canvas = new Canvas();
        }
        canvas.setBitmap(bitmap);
        // Temporarily clobber the cached Canvas in case one of our children
        // is also using a drawing cache. Without this, the children would
        // steal the canvas by attaching their own bitmap to it and bad, bad
        // thing would happen (invisible views, corrupted drawings, etc.)
        // 这里有置空操做,防止其余  子 View 同时也想使用当前的 Canvas 和 对应的 bitmap
        attachInfo.mCanvas = null;
    } else {
        // This case should hopefully never or seldom happen
        canvas = new Canvas(bitmap);
    }

    ...

    mPrivateFlags |= PFLAG_DRAWN;
    if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
            mLayerType != LAYER_TYPE_NONE) {
        mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
    }

    // Fast path for layouts with no backgrounds
    // 这里开始就和硬解同样的逻辑,看是否须要直接调用 dispatchDraw() 
    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;
    }
}
复制代码

buildDrawingCacheImpl() 能够看到软解时 Canvas 的缓存是经过 attachInfo 来实现,也就是说,软解时,建立一次 Canvas 以后,以后每次绘制 都是使用的同一个 Canvas 对象,这个和硬解是有却别的。

到这里,View 动画效果介绍完毕,Animation 会增长 PFLAG_DRAW_ANIMATION 标志并调用 invalidate() 从新绘制。而对于 Animator 来讲,硬解的话,不会调用到 invalidate() 去从新绘制,而是直接更改 renderNode 的相关属性。软解的话,也须要重走 invalidate() 方法。最后再说下 AnimationfillAfter 属性,若是设置了话,View 也会保持动画的最终效果,那这个是怎么实现的呢? 其实就是根据是否要清除动画信息来实现的。这个方法会在 draw() 三个参数的方法中被调用。

void finishAnimatingView(final View view, Animation animation) {
    final ArrayList<View> disappearingChildren = mDisappearingChildren;
    ...
    if (animation != null && !animation.getFillAfter()) {
        view.clearAnimation();
    }
    ...
}
复制代码

最后吐槽一波 View 绘制相关的 flag ,又多又复杂,程序员的小巧思,用着用着 flag 一多,感受这就成灾难了。

相关文章
相关标签/搜索