Android系列View的绘制之draw过程
Posted jzyhywxz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android系列View的绘制之draw过程相关的知识,希望对你有一定的参考价值。
本文将讲述View绘制的最后一个过程——draw过程,继measure和layout过程之后,View已经确认了自身的大小和位置,draw过程将完成View内容的绘制,到此,View的绘制才真正完成。
draw第一步:从DecorView开始
和measure与layout过程一样,draw过程也是由ViewRootImpl对象开始执行的。在ViewRootImpl类的performTraversals
方法中,经由performDraw
、draw
、drawSoftware
一直到调用DecorView的draw
方法,draw过程开始遍历整个View树。
draw方法的六个步骤
View的draw
方法和measure
与layout
方法一样,在自定义View时不用重写draw
方法,而是要重写onDraw
方法,这一点在draw
方法的注释中也给出了说明:
When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method.
下面看下draw
方法的源码:
/* View.draw */
public void draw(Canvas canvas)
/*
* 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
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);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// we're done...
return;
从draw
方法中一开始的注释可以看到,draw
方法是分6步完成的:
1. 绘制背景;
2. 保存画布当前状态为绘制fading edges做准备;
3. 绘制内容;
4. 绘制子View;
5. 绘制dading edges并还原画布状态;
6. 绘制装饰(如滑动条)。
其中,第2和第5步是可选的,上面的代码除去了第2和第5步,因为它们并不常用并且会拖慢绘制过程。第3步是View绘制自身的内容,而第4步是父View绘制子View的过程,draw过程也是在这里传递给子View的。
draw第二步:从父View向子View传递
经过上一节的分析,我们已经知道View是通过onDraw
方法绘制自身的,并且是通过dispatchDraw
方法绘制子View的。
我们看View的onDraw
方法,它是一个空方法,这在我们的意料之中,毕竟不同类型的View在屏幕上展现的内容都不一样,也就是说每个View(不是ViewGroup)都会重写onDraw
方法。
我们再来看dispatchDraw
方法,它在View中也是一个空方法,不过ViewGroup在继承View时重写了此方法,在ViewGroup的dispatchDraw
方法中有这样一段代码:
/* ViewGroup.dispatchDraw */
for (int i = 0; i < childrenCount; i++)
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);
这里在for循环中遍历了每一个子View,并通过drawChild
方法绘制子View的内容:
/* ViewGroup.drawChild */
protected boolean drawChild(Canvas canvas, View child, long drawingTime)
return child.draw(canvas, this, drawingTime);
到此,draw过程就从父View传递到了子View,并重复此过程直到到达View树的叶子节点。
draw第三步:在View处终止
当draw过程传递到View树的叶子节点时,由于已经没有需要绘制的子View,因此draw过程到这里也就完成了。对应的,View的dispatchDraw
方法理所当然是一个空方法。
到这里,View绘制的三个过程都已经介绍完了,如果你还对measure或者layout过程不甚了解,可以阅读另外两篇文章:【Android系列】View的绘制之measure过程和【Android系列】View的绘制之layout过程。
按照惯例,我们也给出draw过程的流程图:
现在,我们再回顾一下View绘制的整个过程:
View的绘制是从ViewRootImpl的performTraversals
方法开始的,并经过measure、layout和draw三个过程才能最终将一个View绘制出来。其中measure过程对View的宽高进行测量,layout过程会确定View在父容器中的放置位置,draw过程负责将View绘制到屏幕上。
坚持就是胜利ヾ(◍°∇°◍)ノ゙
以上是关于Android系列View的绘制之draw过程的主要内容,如果未能解决你的问题,请参考以下文章
Android应用层View绘制流程之measure,layout,draw三步曲