最近两天研究了View是如何绘制的代码,小记如下:
View的绘制,要从ViewRootImpl这个类说起(曾经的ViewRoot,3.0还是4.0之后更名),ViewRootImpl并不是一个View类,那么为什么叫他root呢?因为绘制的动作是从它这里发起的,下面我们会讲它的一个重要的函数performTraversals,其中一个重要作用就是发起了View的绘制,View这棵树就从这里开始生根发芽。
这里补充一下基础知识,android的View层次是一个树状结构,每一个父节点担任起绘制自己和自己儿子节点的任务。
View怎么绘制自己呢
答案是onDraw函数。我们看View的实现中,ondraw是一个空的方法体,凡是它的子类必须实现它以完成绘制自己的功能,例子可以参照TextView。
那么,在这棵View树上,一些节点是有子节点的,他们就是ViewGroup,很形象的名称,我开始还觉得ViewGroup不常用到,实际上像ListView就是一个典型的ViewGroup,它能包含其他的View就是这个道理。
ViewGroup是怎么绘制子节点的
这起源于dispatchDraw函数,这个函数又是被谁调用我们暂且搁着后面再讲。dispatchDraw的作用顾名思义,就是把绘制的事件发放出来,说“你你你,去把自个儿画出来“,这个函数一般会被重载,例子可以参照ListView。如果子节点也是一个ViewGroup,此过程会迭代的走下去,此恨绵绵无绝期阿~~直到最底层的一个叶子节点。
OK,回到正题,我其实想讲的是ViewRootImpl这位祖师爷是如何让这套机制按部就班的进行的,我们来看看ViewRootImpl内部是如何发起绘制的。
最开始我们说到performTraversals这个函数,这个函数确实是牛逼哄哄的,它有800多行,有兴趣的同学可以去看看它老人家。好吧,其实我也没耐心看完,它的作用大致可以分为三大块,Measure(计算视图大小),Layout(开始布局),draw(绘制),前面两大块不是我此篇的重点,最后的一块draw是我今天研究的。
请在performTraversals函数中搜出mView.draw,是的,它就是这棵树绘制的开始啦,进入到View的draw函数中,我们从注释中就能看到它有6个步骤
/*
* 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 //绘制View自己的内容(重要)
* 4. Draw children //绘制子节点(重要)
* 5. If necessary, draw the fading edges and restore layers //如果需要,绘制渐变边缘和恢复图层
* 6. Draw decorations (scrollbars for instance) //绘制一些装饰,比如说滚动条
*/
第三与第四步是关键,那就是绘制自己和绘制子节点。下面的代码告诉我们,绘制开始了。
// 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 (scrollbars)
onDrawScrollBars(canvas);
// we're done...
return;
}
到此为止,视图是如何绘制出来的一个来龙去脉就清晰了。先写到这里把