这篇文章分为下面 5 个部分。html
绘图基础java
这一节会介绍 Android 中的画笔 Paint 和画布 Canvas 的用法。git
路径绘制github
这一节会介绍 Android 中路径 Path 的用法,包括直线、弧线和雷达图等图形的绘制方法。数据结构
文字绘制app
这一节会介绍 Android 中绘制文字的方法,包括粗体、斜体、加载字体等。函数
区域操做post
这一节会介绍 Android 中区域 Region 的用法,包括区域裁剪、区域合并等操做。字体
画布进阶3d
这一节会对画布的用法进行更详细的介绍,包括保存、指向恢复和圆形头像。
对象建立
下面的示例代码会在 onDraw() 方法中建立 Paint、Path、Rect 和 Region等对象,这在实际开发中是禁止的。
由于当 View 须要重绘时会调用 onDraw() 函数,每一次调用 onDraw() 函数都会从新建立这些对象。
这样会引发频繁的 GC,严重时会致使 App 卡顿。
图片清晰度
点击文章中的图片能够查看原图。
示例代码
文中的示例代码源码在文章最下方能够找到。
本节包含内容以下。
咱们这一节来看一下画笔的用法。
本节包含内容以下。
Paint 的 setColor(int color) 方法可用于设置画笔颜色,下面是 color 参数的取值。
一种颜色是由红绿蓝三色合成的,因此 color 只能取 8 位 0xAARRGGBB 样式颜色值。
透明度
A 表示透明度 Alpha,取值范围是 0~255,值越小,图像越透明
红色
R 表示红色 Red,取值范围是 0~255,取值越小红色越少
绿色
G 表示绿色 Green,取值范围是 0~255,取值越小绿色越少
蓝色
B 表示蓝色 Blue,取值范围是 0~255,取值越小蓝色越少
除了手动组合颜色,系统还提供了一个用于解析颜色的类 Color,关于 Color 在后面会有更详细的介绍。
Paint 的 setStyle(Style style) 方法可用于设置画笔样式,下面是 style 参数的取值。
描边
填充
默认样式。
描边且填充
描边与填充叠加在一块儿显示的效果,也就是这个值比填充多了一个描边的宽度。
Paint 的 setStrokeWidth(width) 方法用于设置描边宽度,单位是 px。
注意事项
当画笔的 Style 是 STROKE 或 FILL_AND_STROKE 时画笔宽度才有意义。
画笔 Paint 绘制图形时默认不是抗锯齿的,也就是边边会有锯齿。
Paint 提供了 setAntiAlias() 方法,这个方法能够开启画笔的抗锯齿功能。
下面是两个放大后的圆,右边的圆用的是抗锯齿的画笔绘制的。
上一节演示画笔的同时也演示了画布绘制圆的方法,这一节咱们来看一下画布的其余方法。
本节内容以下。
Canvas 提供了三个设置背景的方法。
须要注意的是,设置画布背景要在其余图形绘制前设置,不然设置好的背景色会覆盖原有的图形。
Canvas 的 drawLine() 方法能够绘制直线,直线的粗细取决于 Paint 的 setStrokeWith(width) 中传入的宽度。
绘制直线须要注意的是,只有当 Style 是 STROKE、FILL_AND_STROKE 时绘制才有效。
Canvas 的 drawPoint() 方法能够用于绘制点,点的大小取决于 setStrokeWith(width) 中传入的宽度。
矩形结构在绘制矩形区域的时候须要用到。
Android 提供了 Rect 和 RectF 类用于存储矩形数据结构,下面是 Rect 和 RectF 的构造函数。
Rect 和 RectF 在于存储的数据类型不一样。
Rect
用于保存 int 类型数值的矩形结构
RectF
用于保存 float 类型数值的矩形结构
Canvas 的 drawPoint() 和 drawRect() 方法均可用于绘制矩形。
下面是这两个方法的区别。
形状
drawPoint()
只能指定矩形中心的坐标,只能画出正方形。
drawRect()
须要指定矩形左上和右下两个点的位置,能够是长方形。
样式
drawPoint()
只能是填充样式。
drawRect()
能够本身选择样式,能够是描边也能够是填充。
下面是 Canvas 中提供用于绘制矩形的三个方法。
Canvas 中提供了一个 drawRoundRect 方法用于绘制圆角矩形,圆角矩形的四个角是椭圆的一角,下面是它的基本用法。
Color 类是 Android 中与颜色有关的类。
本节内容以下。
Color 中定义了下面的颜色常量值。
除了颜色常量外,Color 还提供了一些构造颜色的方法。
alpha << 就是进 24 位,当咱们调用 Color.argb(255, 0, 0,0 0) 时,转换后的 16 进制颜色值就是 0xFF000000。
路径 Path 指的是画笔画出来的一条不间断的曲线,本节会讲解直线和弧线路径,除了这两种方式外还有不少方法能够实现不少效果。
drawPath 是 Canvas 绘制路径的方法。
本节内容以下。
上面是一段绘制直线路径的代码,这里须要注意的是,上面代码中的画笔样式设为描边,默认是填充的。
也就是在默认状况下,多条线相连造成闭环后,中间的区域会被填充。
绘制直线路径涉及:起点、终点和闭环。
起点
Path 的 moveTo() 用于指定直线路径的起点,参数 x1 和 y1 是起点坐标值。
终点
Path 的 lineTo() 用于指定直线路径的起点,参数 x2 和 y2 是终点坐标值,也是下一次绘制直线的起点。
闭环
Path 的 close() 方法用于造成闭环。
若是连续画了几条直线,但没有造成闭环,调用 close() 函数会把路径首尾链接起来造成闭环,至关因而帮咱们画多一条直线。
若是只画了一条直线,那 close() 方法是不会起做用的。
Path 的 arcTo() 方法可用于绘制弧线,弧线在这里指的是椭圆上截取的一部分。
本节内容以下。
这里的椭圆底色默认是不存在的,在这里画出来主要是为了突出弧线。
在这里咱们要注意的是,弧线默认是填充的,更准确的来讲 drawArc() 方法是切出椭圆中的一块。
若是咱们只想要一条线的话,就要本身设置描边样式和描边宽度。
除了样式之外,绘制弧线要注意的另一点就是起点。
改变起点
若是咱们调用了 moveTo() 改变了路径的起点,那弧线就会从 moveTo() 接收到的坐标开始绘制。
重置起点
若是咱们想重置起点到弧线正常该开始的位置,咱们能够把 forceMoveTo 设为 true。
arcTo 中有两个跟角度有关的参数 startAngle 和 sweepAngle,这两个参数分别表明起始角和扫描角。
起始角
起始角指定弧线从哪里开始画起。
扫描角
扫描角能够当作是弧线的长度。
Path 中定义了的三个 arcTo() 函数,下面是 arcTo() 函数中主要四个参数的含义。
oval
生成椭圆的矩形
startAngle
弧开始的角度,以 X 轴正方向为 0°
sweepAngle
弧持续的角度
forceMoveTo
重置起点,把绘制弧线的起点从 moveTo 的坐标重置到 startAngle 的位置。
本节内容以下。
Path 提供了 addXXX 函数用于添加路径,添加的路径能够是不连续的,还能够是曲线。
下面是 Path 中提供的一些添加路径的方法。
下面是添加路径的示例。
在添加路径的函数中有一些函数有 Direction 参数,这个 Direction 就是绘制的方向。
方向分为顺时针逆时针 CCW(Counter-Clockwise)。
下面是不一样方向的绘制示例,这个示例中用到了 drawTextOnPath 方法,这个方法在讲绘制文字的时候会有更详细的介绍,如今咱们先看绘制方向对绘制效果的影响。
路径 Path 的填充模式与画笔 Paint 的填充模式不一样,Path 的填充模式是指填充 Path 的哪部分。
在 Path 中有一个 FillType 枚举类,其中定义了 4 种填充类型。
下面是这四种填充类型的示例。
Android 提供了两个重置路径的方法,让咱们能够重复使用 Path 对象。
路径 Path 一旦被重置,其中保存的全部路径都会被清空,这样就不须要从新建立一个 Path 对象了。
reset()
reset() 函数相似于新建一个路径对象,除了填充类型 FillType 之外,Path 的全部数据都会被回收并从新分配。
rewind()
rewind() 函数会清除 FillType 和全部的路径,保留内部数据结构,以便更快地重用。
好比咱们须要重复绘制一类线段,它们的点和数量相等,使用 rewind() 函数能够保留装载点数据的数据结构,效率更高。
要注意的是,只有重复绘制相同路径时,这些数据结构才是可复用的。
本节包含以下内容。
在控件大小发生变化时,onSizeChanged() 会被回调并获得最新的控件大小,因此咱们须要重写这个方法。
网状路径的大小占当前控件的 90%,因此半径为:宽高最小值 ÷ 2 × 0.9。
而后根据中心点,分别绘制雷达网格、网格中线、数据图。
drawPolygon 涉及到三角函数,下面是公式计算示意图。
下面是公式的具体实现。
本文包含以下内容。
本节包含内容以下。
下面是几个 Paint 中设置文字绘制效果的方法。
咱们能够经过 Paint 的 setTextAlign(align) 方法来设置绘制文字时的对齐方向。
Paint 中有一个枚举类 Align,在 Align 中定义了三种对齐模式:LEFT、CENTER、RIGHT,这三种模式分别表明左对齐,居中对齐和右对齐。
上一节咱们看到了Canvas 的 drawText() 方法,下面咱们来看一下 Canvas 提供的其余绘制文本的方法。
本节包含内容以下。
在讲解路径绘制方向时咱们就已经用到过 Canvas 提供了 drawTextOnPath() 方法,在这里咱们看一下这个方法中的偏移参数。
Paint 中提供了 setTypeFace(typeFace) 方法同于设置字体样式。
TypeFace 是专门用于设置字体样式的,咱们能够指定系统提供的字体也能够指定自定义字体。
当建立 TypeFace 类时,能够指定是正常字体、斜体或者粗体,当指定样式中没有相关文字的样式时,就会用系统默认的样式显示,通常默认是宋体。
本节包含以下内容。
TypeFace 中提供了下面三种字体,这三种字体对中文的支持不是很好,当遇到不支持的文字时,会用系统默认字体来写。
下面是这三种字体的显示效果。
除了能够选择特定字体之外,咱们还能够经过 defaultFromStyle() 方法选择特定具体的某种样式。
TypeFace 中还定义了下面四种字体样式。
下面是这四种样式的显示效果。
Android 提供了下面三种加载自定义字体的方式。
1. 根据字体名加载
TypeFace 中提供了一个用于根据字体名加载字体的 create() 方法,好比下面这行代码这样。
因为模拟器上没有别的字体,在这里就不演示了,你们有兴趣的能够本身尝试下。
2. 根据资源名加载
假如咱们从网上下了个 ttf 字体文件,想放在包中直接使用,咱们能够在 app/src/main 目录下建一个 assets 目录,再建一个 fonts 目录,而后把 ttf 文件放到里面。
而根据资源名加载字体的方式就是 TypeFace 的 createFromAsset() 方法。
3. 根据文件名加载字体
从资源中加载字体的弊端就是会让 APK 包变大,若是咱们把字体下载到本地的话就能够避免这个问题。
Paint 提供了三种获取文字宽高的方法,下面咱们来看一下这几个方法的用法。
本节内容以下。
区域指的是 Region 类,Region 是一块任意形状的封闭图形,这一节咱们来看一下 Region 提供的一些操做区域的方法。
本节包含以下内容。
直接构造区域指的是用 Region 的构造函数建立区域,下面是 Region 的四个构造函数。
下面是一个绘制 Region 的简单示例,因为 Canvas 中没有绘制 Region 的方法,因此咱们在这里本身定义一个 drawRegion() 方法来绘制 Region。
Region 可用于绘制各类各样的形状,咱们先看一下怎么绘制一个简单的正方形 Region。
Region 提供了一个 union(rect) 函数,传入目标矩阵到 union 函数中就能实现合并区域。
Region 中声明了下面的 5 个设置区域的方法。
裁剪区域须要先建立一个空 Region,而后调用 setPath 方法,下面是裁剪区域的操做,椭圆路径和裁剪区域相交的区域就是裁剪结果。
本节以下内容。
1. 集合运算方法
除了 union 之外,Region 还提供了下面几个区域操做方法,并且 union 自己也是调用了 op 方法。
2. 集合运算类型
在上面这些方法中比较重要的是 Op 参数,Op 是 Region 中定义的枚举类,是集合运算操做。
在这里,集合中的元素就是 Region 矩阵范围中的子矩阵。
**3. 集合运算示例 **
下面是这些集合运算的示例。
上一节咱们讲了怎么用画布 Canvas 绘制各类图形,画布除了能用来绘制各类图形之外,咱们还能对画笔进行变换和裁剪。
本节内容以下。
画布 Canvas 提供了一个可用于平移的方法 translate(),画布的原始状态是以左上角为圆点,向右是 X 轴正方向,向下是 Y 轴正方向。
因为画布的左上角是坐标轴的原点(0, 0),因此平移画布后,坐标系也会被平移。
平移后的画笔的左上角是新的坐标原点。
合成画布与屏幕的操做是由系统进行的,这一节咱们来看一下这个操做的简单介绍。
本节内容以下。
咱们每次在画布上画图时,系统会先产生一个透明图层,在这个图层上画图,花完后再覆盖在屏幕上。
当咱们绘制红色矩形时,会产生另外一个新的 Canvas 透明图层,此时画布坐标改变了,因此绘图方式以下图所示。
因为 Canvas 已经平移了 350 像素,因此画图时是以新原点来产生视图的,而后再合成到屏幕上。
当屏幕移动后,有一部分超出屏幕的范围,超出范围的图像是不显示的。
使用 Canvas 的绘制方法有下面三个须要注意的点。
生成新图层
每次调用绘制方法 drawXXX 时,都会产生一个新的 Canvas 透明图层。
操做不可逆
调用了绘制方法前,平移和旋转等函数对 Canvas 进行了操做,那么这个操做是不可逆的,每次产生的画布的最新位置都是这些操做后的位置。
超出不显示
在 Canvas 图层与屏幕合成时,超出屏幕范围的图像是不会显示出来的
画布裁剪是指用 Canvas 的 clipXXX 函数与矩形、路径和区域取交、并、差等集合运算来得到最新的画布形状。
除非使用了保存和恢复函数,不然裁剪操做是不可逆的,也就是一旦裁剪就没法恢复。
在 Canvas 中定义了下面几个裁剪函数。
下面是一个裁剪示例。
在前面咱们说到了画布的操做是不可逆的,这会形成不少麻烦。
好比为了实现一些效果须要对画布进行操做,但画布状态改变后又影响了后面的绘制效果。
由于这个缘由,画布提供了保存和恢复功能,这两个功能对应的方法是 save() 和 restore(),每调用一次 save() 就会把当前画布状态保存到栈中。
本节内容以下。
画布单次保存恢复的操做比较简单,咱们来看一下画布屡次保存和屡次恢复的效果。
指向恢复指的是恢复到特定的状态,好比咱们上面这个例子,假如咱们要恢复到蓝色400,那咱们要调用三次 resoter()。
为了解决这个问题,Canvas 提供了另外一个恢复画布状态的函数 restoreToCount(count),下面咱们来看一下这个函数的用法。
下面咱们来看一下怎么用画布的保存和恢复功能实现绘制圆形头像后,再把画布恢复回来。
本节包含以下内容。
1. 初始化
这里之因此要禁用硬件加速,是由于硬件加速是使用的 OpenGL 函数完成实际绘制,而 OpenGL 并不能彻底支持原始绘制函数,好比 clipPath() 在开启硬件加速的状况下只有在 API 18 以上的系统才会生效。
2. 绘制头像
3. 去除锯齿
clipPath 实现的圆形图像是有锯齿的,咱们能够用 PorterDuffXfermode 来实现无锯齿的圆形图片。