draw(绘制)过程的作用是将View绘制到屏幕上面。View中有 draw() 方法和 onDraw() 方法,但onDraw()方法是空方法;ViewGroup中没有draw()方法,也没有onDraw()方法。由此可以推测出:ViewGroup的绘制过程调用的流程和View的绘制过程流程是相同的,且和onLayout()、onMeasure()方法一样,因为不同的ViewGroup子类有不同的绘制方式,因此将onDraw()方法下放到ViewGroup的子类中实现。接下来看一下View类的draw()方法的源码:
/** * 在给定的Canvas画布上手动渲染这个View及其所有子元素; * 在调用这个方法之前,这个View必须已经经过了layout布局过程; * 在自定义View的时候,不能重写这个方法,而应该重写onDraw()方法。 * * @param canvas View渲染的目标画布 */ @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; /* * 这种遍历绘制的方法需要执行几个步骤,且必须按适当的顺序执行 * 1. 绘制背景 * 2. (按需执行)保存画布层以应对褪色 * 3. 绘制View的内容 * 4. 绘制View中的子元素 * 5. (按需执行)绘制褪色边缘并恢复画布层 * 6. 绘制装饰物(前景、滚动条等) */ // Step 1、绘制背景 int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } // 一般情况下,都会尽量跳过Step2和Stop5 final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3、绘制View中的内容 if (!dirtyOpaque) onDraw(canvas); // Step 4、绘制View中的子元素 dispatchDraw(canvas); // Overlay是View内容的一部分,应该画在前景的底下(先于前景绘制) if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6、绘制装饰物,如前景、滚动条 onDrawForeground(canvas); // 完成绘制 return; } /* * 我们在下面做了完整的例程: * 这是一种不常见的情况,在这种情况中渲染速度不是很重要,因此我们将上面的一些流程又重复了一遍 */ // ......代码省略...... }
从上面的代码可以看出,View的绘制过程总共分为六个步骤,但大多数情况下,第二步和第五步都会被省略,因此,我们在这里只分析1、3、4、6步骤。这样,View的绘制流程可以总结为: 绘制背景->绘制内容->绘制子元素->绘制其他(前景、滚动条等) ,对应的四个方法分别是: drawBackground() 、 onDraw() 、 dispatchDraw() 、 onDrawForeground() 。其中,onDraw()方法和dispatchDraw()方法在View类中没有实现,需要我们在自定义View中自行重写。