前言
- 自定义View是Android开发者必须了解的基础
- 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全、思路不清晰、无源码分析、简单问题复杂化等等
- 今天,我将全面总结自定义View原理中的draw过程,我能保证这是市面上的最全面、最清晰、最易懂的
- 文章较长,建议收藏等充足时间再进行阅读
目录
1. 知识基础
具体请看我写的另外一篇文章:自定义View基础 - 最易懂的自定义View原理系列
2. draw过程作用
绘制View视图
3. draw过程详解
同measure、layout过程一样,draw过程根据View的类型分为两种情况:
1. 如果View = 单一View,则仅绘制本身View;
2. 如果View = VieGroup(包含子View),除了绘制自身View外,还需要绘制子View。
接下来,我将详细分析这两种情况下的draw过程。
3.1 单一View的draw过程
3.1.1 应用场景
在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View、SurfaceView等
特点是:不包含子View。
3.1.2 原理(步骤)
- 步骤1:View绘制自身(含背景、内容);
- 步骤2:绘制装饰(滚动指示器、滚动条、和前景)
3.1.3 单一View的的具体draw过程
如下图所示:
下面我将一个个方法进行详细分析。
步骤1: draw()
作用:根据给定的 Canvas 自动渲染 View(包括其所有子 View)。
在调用该方法之前必须要完成 layout 过程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
下面,我们继续分析在draw()中
调用的drawBackground()
、 onDraw()
、dispatchDraw()
、onDrawScrollBars(canvas)
步骤2: drawBackground()
- 作用:绘制自身View的背景
- 源码分析如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
步骤3: onDraw()
- 作用:绘制自身View的内容
- 源码分析如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
请记住:自定义View中必须 且 只需要复写onDraw()
。
步骤4: dispatchDraw()
- 作用:绘制子View
- 源码分析:
- 1
- 2
- 3
- 4
- 5
步骤5: onDrawForeground()
- 作用:绘制装饰(滚动指示器、滚动条、和前景)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
至此,单一View的draw过程已经分析完毕。
3.1.4 总结
单一View的draw过程解析如下:
只需要绘制自身View
3.2 ViewGroup的draw过程
3.2.1 应用场景
利用现有组件根据特定布局方式来组成新的组件,大多继承自ViewGroup或各种Layout
特点:含有子View
3.2.2 原理(步骤)
步骤1: ViewGroup绘制自身(含背景、内容);
步骤2: ViewGroup遍历子View并绘制包含的所有子View;
类似于单一View的draw过程
步骤3: ViewGroup绘制装饰(滚动指示器、滚动条、和前景)
这样自上而下、一层层地传递下去,直到完成整个View树的draw过程
3.2.3 ViewGroup的具体draw过程
如下图所示:
下面我将对每个步骤和方法进行详细分析。
步骤1:draw()
-
作用:根据给定的 Canvas 自动渲染 View
在调用该方法之前必须要完成 layout 过程
-
源码分析:(与单一View draw过程的draw()类似)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
步骤2:drawBackground()、步骤3:onDraw()、步骤5:onDrawForeground()
- 与单一View的draw过程类似,详细请回看上面描述。
- 下面直接进入与单一View draw过程最大不同的
步骤4:dispatchDraw()
。
步骤4: dispatchDraw()
- 作用:遍历子View并绘制
- 源码分析:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
至此,ViewGroup的draw过程已经分析完毕。
3.2.4 总结
对于ViewGroup的draw过程流程如下:
4. 其他细节问题:View.setWillNotDraw()
View 中有一个特殊的方法:setWillNotDraw()
- 1
- 2
- 3
- 该方法用于设置 WILL_NOT_DRAW 标记位
- 该标记位的作用是:当一个View不需要绘制内容时,系统进行相应优化
默认情况下:View 不启用该标记位(设置为true);ViewGroup 默认启用(设置为false)
应用场景:
-
setWillNotDraw参数设置为true:当自定义View继承自 ViewGroup 、且本身并不具备任何绘制时,设置为 true 后,系统会进行相应的优化。
-
setWillNotDraw参数设置为false:当自定义View继承自 ViewGroup 、且需要绘制内容时,那么设置为 false,来关闭 WILL_NOT_DRAW 这个标记位。
5. 总结
- 对于ViewGroup的draw过程
步骤1: ViewGroup绘制自身(含背景、内容);
步骤2: ViewGroup遍历子View并绘制包含的所有子View;
类似于单一View的draw过程
步骤3: ViewGroup绘制装饰(滚动指示器、滚动条、和前景)
-
对于View的draw过程
只需要绘制自身(含背景、内容)+装饰即可 -
一个图总结自定义View - draw过程,如下图:
- 接下来可以开始看自定义View的原理了:
自定义View基础 - 最易懂的自定义View原理系列(1)
自定义View Measure过程 - 最易懂的自定义View原理系列(2)
自定义View Layout过程 - 最易懂的自定义View原理系列(3)
自定义View Draw过程- 最易懂的自定义View原理系列(4) -
接下来我将继续对自定义View的应用进行讲解,有兴趣的可以继续关注Carson_Ho的安卓开发笔记
-
简书id:Carson_Ho