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