开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
dispatchDraw
- 第三段
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();
有一个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;
- 第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);
}
}
- 第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;
}
}
- 第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);
}