前言
接着分析Android View的工作原理,前面已经说了重要3步中的2步,本篇内容说一下最后一步,draw流程。
本系列文章:
Android View的工作原理1 — measure基础知识 – 掘金 (juejin.cn)
Android View的工作原理2 — measure流程 – 掘金 (juejin.cn)
Android View的工作原理3 — layout流程 – 掘金 (juejin.cn)
正文
其实View的绘制涉及很多知识点,也是自定义View中最关键的一步,它需要通过canvas即画布绘制出需要的View,关于具体的绘制方法在本篇内容不做细致说明,在后续文章我们抽丝剥茧得再分析。
draw过程
至于什么地方调用draw开始绘制的,这篇文章就不再说明了,具体可以查看前面2篇文章,也就是从ViewRootImpl开始,会直接调用view的draw方法。
对于draw就比较简单了,大致可以分为下面几步:
1 绘制背景 background.draw(canvas)
2 绘制自己 onDraw
3 绘制children dispatchDraw
4 绘制装饰 onDrawScrollbars
其中最主要的就是第2和第3步,还是拿mDecorView举例,它是一个FrameLayout即ViewGroup,当调用mDecorView.draw时,它会先调用onDraw绘制自己,然后发现它自己有children,再去调用dispatchDraw去绘制子View。
源码解析
话不多说,还是直接看一下源码:
//View中的代码
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)
* 7. If necessary, draw the default focus highlight
*/
// 步骤1 绘制背景
drawBackground(canvas);
// ...
if (!verticalEdges && !horizontalEdges) {
//绘制自己
onDraw(canvas);
//绘制子View
dispatchDraw(canvas);
//绘制前景
onDrawForeground(canvas);
//...
return;
}
//...
}
复制代码
源码十分简单,几乎可以不用看,就记住一句话即可:在onDraw中绘制自己,然后在需要时调用dispatchDraw绘制子View,而这里绘制子View就是通过遍历所有子元素的draw方法即可完成,如此draw事件就一层一层地传递了下去。
当然这2个方法在View中都是抽象方法:
protected void onDraw(Canvas canvas) {
}
复制代码
protected void dispatchDraw(Canvas canvas) {
}
复制代码
但是在ViewGroup中则不是的,在ViewGroup中dispatchDraw就有了实现,实现也就是前面说的遍历子View进行传递绘制:
//ViewGroup的方法
@Override
protected void dispatchDraw(Canvas canvas) {
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
//遍历子View
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
for (int i = 0; i < childrenCount; i++) {
//...
//绘制子View
drawChild(canvas, child, drawingTime)
//...
}
//...
}
复制代码
上面代码省略很多,主要就是遍历子View,调用drawChild方法:
//直接就是调用子View的draw方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
复制代码
可以看出draw事件的传递还是很容易的,主要涉及2个方法。
总结
本篇文章只是一个引子,关于绘制的具体方法后面单独会出一个系列来说,比如canvas概念、如何绘制等等。