android draw ondraw(二)

140 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

dispatchDraw

  1. 第三段
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;

if (clipToPadding) {
    clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
    canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
            mScrollX + mRight - mLeft - mPaddingRight,
            mScrollY + mBottom - mTop - mPaddingBottom);
}

//每次绘制前都先初始化flag
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;

boolean more = false;
//获取绘制时间,这个时间的设置是在viewrootimpl的performDraw(绘制分发起始点)开始时设置的,也就是说在这之前,这个函数会返回0。
final long drawingTime = getDrawingTime();

image.png

有一个clipToPadding我们单独把这一段拎出来

if (clipToPadding) {
    clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
    canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
            mScrollX + mRight - mLeft - mPaddingRight,
            mScrollY + mBottom - mTop - mPaddingBottom);
}

mRight、mBottom这些参数再layout中进行初始化,反映了当前view相对于父view的真实位置。

首先是canvas通过save保存一下当前canvas属性,当绘制完子view之后再调用restore恢复当前保存的属性。因为还要调用其他view的dispatchDraw也同样会使用这一个canvas,所以需要恢复当前view对canvas属性操作。要注意的是clipRect并不会改变canvas的初始坐标,只是超出rect的部分不显示。

默认clipToPadding=true;

  1. 第4段
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
int transientIndex = transientCount != 0 ? 0 : -1;
//preorderedList自定义绘制顺序列表
final ArrayList<View> preorderedList = usingRenderNodeProperties
        ? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
        && isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
    //绘制TransientView,TransientView是一个没有父类的view不参与布局,不参与事件响应的view
    //mTransientIndices的value值是从小往大排列的
    while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
        final View transientChild = mTransientViews.get(transientIndex);
        if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                transientChild.getAnimation() != null) {
                //开始绘制TransientView
                //more表示如果发出 invalidate() 则为真
            more |= drawChild(canvas, transientChild, drawingTime);
        }
        transientIndex++;
       
        if (transientIndex >= transientCount) {
        //已经全部绘制完了,将transientIndex置为-1作为绘制完的标记
            transientIndex = -1;
        }
    }
    //绘制view,也就是TransientView会先于子view绘制
    //用于返回该view的绘制顺序,如果需要自定义view的绘制顺序,覆盖getChildDrawingOrder方法,默认返回i。
    final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
    //如果preorderedList为null,则返回mChildren[childIndex],不为null则返回preorderedList.get(childIndex)
    final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
    //如果该子view可视或者通过view.setAnimation设置过动画那么就执行绘制
    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
        more |= drawChild(canvas, child, drawingTime);
    }
}
  1. 第5段
//当子view绘制完之后,如果还存在transientview没有绘制完,则把剩下的transientview全部绘制完
while (transientIndex >= 0) {
    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;
    }
}
  1. 第6段
//执行clear,使取消对view的引用,使view可以被回收
if (preorderedList != null) preorderedList.clear();

//mDisappearingChildren表示已经从mchidlren中移除,但是仍然需要做动画的那些view,添加这类view需要通过这个方法addDisappearingView
if (mDisappearingChildren != null) {
    final ArrayList<View> disappearingChildren = mDisappearingChildren;
    final int disappearingCount = disappearingChildren.size() - 1;
    for (int i = disappearingCount; i >= 0; i--) {
        final View child = disappearingChildren.get(i);
        more |= drawChild(canvas, child, drawingTime);
    }
}
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
//在开发者模式中开启的layout显示,就会进去这个if语句
if (debugDraw()) {
    onDebugDraw(canvas);
}
//恢复由于clipRect导致的范围裁切
if (clipToPadding) {
    canvas.restoreToCount(clipSaveCount);
}