我的GitHub | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|
baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目录
扔物线自定义 View 系列教程总结-2
全文整理自 扔物线(HenCoder)自定义 View 系列文章
重新整理的目标:
- 内容压缩:去除活跃气氛的段子、图片,去除无意义的解释、代码,去除不刚兴趣的内容,压缩比至少 50%
- 排版优化:更清晰的结构,更精简的标题,更规范的缩进、标点符号、代码格式,好的结构才能更好的吸收
- MarkDown:以标准的 MarkDown 格式重新编排,纯文本更易迭代维护
扔物线自定义 View 系列教程分
绘制
、布局
和触摸反馈
三部分内容。
Paint 详解
本部分的内容,只要做到「知道有这么个东西」,在需要用到的时候能想起来这个功能能不能做、大致用什么做就好,至于具体的实现,到时候拐回来再翻一次就行了。
Paint
的 API 大致可以分为 4 类:
- 颜色
- 效果
- drawText() 相关
- 初始化
一、颜色类方法
Canvas
绘制的内容,有三层对颜色的处理:
- 直接设置颜色的 API 用来给图形和文字设置颜色
setColorFilter()
用来基于颜色进行过滤处理setXfermode()
用来处理源图像和View
已有内容的关系。
第1层:像素的基本颜色
像素的基本颜色,根据绘制内容的不同而有不同的控制方式:
Canvas.drawColor/RGB/ARGB()
是直接写在方法的参数里的Canvas.drawBitmap()
是直接由Bitmap
对象来提供的- 图形和文字的颜色需要使用
paint
参数来设置,Paint
设置颜色的方法有两种:- 使用
Paint.setColor/ARGB()
来设置颜色 - 使用
Paint.setShader(Shader shader)
来指定着色方案
- 使用
Shader 着色器
Shader
「着色器」不是 Android 独有的,它是图形领域里一个通用的概念,它和直接设置颜色的区别是,着色器设置的是一个颜色方案
,或者说是一套着色规则
。
当设置了
Shader
之后,Paint
在绘制图形和文字
时就不使用setColor/ARGB()
设置的颜色了,而是使用Shader
的方案中的颜色。
LinearGradient
线性渐变:设置两个点和两种颜色,以这两个点作为端点,使用两种颜色的渐变来绘制颜色RadialGradient
辐射渐变:从中心向周围辐射状的渐变SweepGradient
扫描渐变BitmapShader
用Bitmap
的像素来作为图形或文字的填充ComposeShader
混合着色器
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
//渐变的两个端点的位置,端点的颜色,端点范围之外的着色规则
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)
//辐射中心的坐标,辐射半径,辐射中心的颜色,辐射边缘的颜色,辐射范围之外的着色模式
SweepGradient(float cx, float cy, int color0, int color1) //扫描的中心,扫描的起始/终止颜色
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY) //横/纵向的着色模式
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
TileMode
渲染模式 TileMode
一共有 3 个值可选:
CLAMP
在端点之外延续端点处的颜色MIRROR
镜像模式REPEAT
重复模式
案例
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.batman);
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
canvas.drawCircle(300, 300, 200, paint);
// 第一个 Shader:头像的 Bitmap(目标图像)
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman);
Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// 第二个 Shader:LOGO 的 Bitmap(源图像)
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo);
Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// ComposeShader:结合两个 Shader。SRC_OVER:把源图像(logo)铺在目标图像(batman)上
Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER);
paint.setShader(shader);
canvas.drawCircle(300, 300, 300, paint);
注意:上面这段代码中的
ComposeShader()
在硬件加速下是不支持两个相同类型的Shader
的,所以这里也需要关闭硬件加速才能看到效果。
PorterDuff.Mode 叠加模式
PorterDuff.Mode
是用来指定两个图像共同绘制时的颜色策略的。它是一个 enum,不同的 Mode
可以指定不同的策略。
「颜色策略」的意思,就是说把源图像绘制到目标图像处时应该怎样确定二者结合后的颜色,而对于 ComposeShader(shaderA, shaderB, mode)
这个具体的方法,就是指应该怎样把 shaderB
绘制在 shaderA
上来得到一个结合后的 Shader
。
如果把上面例子中的参数 mode
改为 PorterDuff.Mode.DST_OUT
,就会变成挖空效果:
如果把 mode
改为 PorterDuff.Mode.DST_IN
,就会变成蒙版抠图效果:
具体来说, PorterDuff.Mode
一共有 17 个,可以分为两类:
- Alpha 合成 (Alpha Compositing)
- 混合 (Blending)
Alpha 合成
第一类,Alpha 合成,其实就是 「PorterDuff」 这个词所指代的算法。「PorterDuff」 并不是一个具有实际意义的词组,而是两个人的姓。这两个人当年共同发表了一篇论文,描述了 12
种将两个图像共同绘制的操作(即算法)。而这篇论文所论述的操作,都是关于 Alpha 通道(也就是我们通俗理解的「透明度」)的计算的,后来人们就把这类计算称为 Alpha 合成 (Alpha Compositing) 。
效果直接盗 Google 的官方文档:
混合 Blending
第二类,混合,也就是 Photoshop 等制图软件里都有的那些混合模式。这一类操作的是颜色本身而不是 Alpha
通道,并不属于 Alpha
合成,所以和 Porter 与 Duff 这两个人也没什么关系,不过为了使用的方便,它们同样也被 Google 加进了 PorterDuff.Mode
里。
效果依然盗 Google 的官方文档
总结
从效果图可以看出,Alpha 合成类的效果都比较直观,基本上可以使用简单的口头表达来描述它们的算法。而混合类的效果就相对抽象一些,只从效果图不太能看得出它们的着色算法,更看不出来它们有什么用。不过没关系,你如果拿着这些名词去问你司的设计师,他们八成都能给你说出来个 123。
所以对于这些 Mode
,正确的做法是:
- 对于 Alpha 合成类的操作,掌握他们,并在实际开发中灵活运用
- 对于混合类的,你只要把它们的名字记住就好了,这样当某一天设计师告诉你「我要做这种混合效果」的时候,你可以马上知道自己能不能做,怎么做
第2层:setColorFilter 颜色过滤
Paint
还可以通过 setColorFilter(ColorFilter colorFilter)
来为绘制设置颜色过滤
。
颜色过滤的意思,就是为绘制的内容设置一个统一的过滤策略,然后 Canvas.drawXXX()
方法会对每个像素都进行过滤后再绘制出来。
有色玻璃透视:
ColorFilter
并不直接使用,而是使用它的子类。它共有三个子类:LightingColorFilter
PorterDuffColorFilter
和 ColorMatrixColorFilter
。
LightingColorFilter 光照效果
LightingColorFilter
可以用来模拟简单的光照效果。
构造方法是 LightingColorFilter(int mul, int add)
,参数里的 mul
和 add
都是和颜色值格式相同的 int 值,其中 mul
用来和目标像素相乘
, add
用来和目标像素相加
:
R' = R * mul.R / 0xff + add.R
G' = G * mul.G / 0xff + add.G
B' = B * mul.B / 0xff + add.B
一个「保持原样」的「基本 LightingColorFilter
」, mul
为 0xffffff
, add
为 0x000000
(也就是0),那么对于一个像素,它的计算过程就是:
R' = R * 0xff / 0xff + 0x0 = R // R' = R
G' = G * 0xff / 0xff + 0x0 = G // G' = G
B' = B * 0xff / 0xff + 0x0 = B // B' = B
基于这个「基本 LightingColorFilter
」,你就可以修改一下做出其他的 filter。比如,如果你想去掉原像素中的红色,可以把它的 mul
改为 0x00ffff
(红色部分为 0 ) ,那么它的计算过程就是:
R' = R * 0x0 / 0xff + 0x0 = 0 // 红色被移除
G' = G * 0xff / 0xff + 0x0 = G
B' = B * 0xff / 0xff + 0x0 = B
或者,如果你想让它的绿色更亮一些,就可以把它的 add
改为 0x003000
(绿色部分为 0x30 ),那么它的计算过程就是:
R' = R * 0xff / 0xff + 0x0 = R
G' = G * 0xff / 0xff + 0x30 = G + 0x30 // 绿色被加强
B' = B * 0xff / 0xff + 0x0 = B
PorterDuffColorFilter 叠加效果
这个 PorterDuffColorFilter
的作用是使用一个指定的颜色
和一种指定的 PorterDuff.Mode
来与绘制对象进行合成。
PorterDuffColorFilter(int color, PorterDuff.Mode mode) //指定的颜色,指定的 Mode
ColorMatrixColorFilter 颜色矩阵
ColorMatrixColorFilter
使用一个 ColorMatrix
来对颜色进行处理。ColorMatrix
内部是一个 4x5 的矩阵:
[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]
通过计算,ColorMatrix
可以把要绘制的像素进行转换。对于颜色 [R, G, B, A]
,转换算法是这样的:
R' = a*R + b*G + c*B + d*A + e;
G' = f*R + g*G + h*B + i*A + j;
B' = k*R + l*G + m*B + n*A + o;
A' = p*R + q*G + r*B + s*A + t;
ColorMatrix
有一些自带的方法可以做简单的转换,例如可以使用 setSaturation(float sat)
来设置饱和度;另外你也可以自己去设置它的每一个元素来对转换效果做精细调整。
第3层:setXfermode
Xfermode
其实就是 Transfer mode
,用 "X" 来代替 "Trans" 是一些美国人喜欢用的简写方式。
- 严谨地讲,
Xfermode
指的是你要绘制的内容
和Canvas
的目标位置的内容
应该怎样结合计算出最终的颜色。 - 通俗地说,其实就是要你以
绘制的内容
作为源图像,以 View 中已有的内容
作为目标图像,选取一个PorterDuff.Mode
作为绘制内容的颜色处理方案。
Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermode
PorterDuff.Mode
在 Paint
一共有三处 API ,它们的工作原理都一样,只是用途不同:
创建 Xfermode
的时候其实是创建的它的子类 PorterDuffXfermode
。而事实上, Xfermode
也只有这一个子类。所以在设置 Xfermode
的时候不用多想,直接用 PorterDuffXfermode
吧。
其实在更早的 Android 版本中,
Xfermode
还有别的子类,但别的子类现在已经deprecated
了,如今只剩下了PorterDuffXfermode
。
Xfermode
使用很简单,不过有两点需要注意:
- 使用离屏缓冲(Off-screen Buffer)
离屏缓冲
对于上面的案例,按照逻辑我们会认为,在第二步画圆的时候,跟它共同计算的是第一步绘制的方形。但实际上,却是整个 View
的显示区域都在画圆的时候参与计算,并且 View
自身的底色并不是默认的透明色,而且是遵循一种迷之逻辑
,导致不仅绘制的是整个圆的范围,而且在范围之外都变成了黑色
。就像这样:
这......那可如何是好?
要想使用 setXfermode()
正常绘制,必须使用离屏缓存
(Off-screen Buffer) 把内容绘制在额外的层上,再把绘制好的内容贴回 View 中。也就是这样:
通过使用离屏缓冲,把要绘制的内容单独绘制在缓冲层, Xfermode
的使用就不会出现奇怪的结果了。使用离屏缓冲有两种方式:
Canvas.saveLayer()
View.setLayerType()
Canvas.saveLayer
Canvas.saveLayer()
可以做短时的离屏缓冲。使用方法很简单,在绘制代码的前后各加一行代码,在绘制之前保存,绘制之后恢复:
int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG); //在绘制之前保存
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermode
canvas.restoreToCount(saved); //在绘制之后恢复
View.setLayerType
View.setLayerType()
是直接把整个 View
都绘制在离屏缓冲中。setLayerType(LAYER_TYPE_HARDWARE)
是使用 GPU 来缓冲, setLayerType(LAYER_TYPE_SOFTWARE)
是直接直接用一个 Bitmap
来缓冲。
关于
Canvas.saveLayer()
和View.setLayerType()
,其实我们常用来开启/关闭硬件加速,这里就不细讲它们的意义和原理了,后面会专门讲它们。
如果没有特殊需求,可以选用第一种方法 Canvas.saveLayer()
来设置离屏缓冲,以此来获得更高的性能。
更多关于离屏缓冲的信息,可以看官方文档中对于 硬件加速 的介绍。
控制好透明区域
使用 Xfermode 来绘制的内容,除了注意使用离屏缓冲,还应该注意控制它的透明区域不要太小,要让它足够覆盖到要和它结合绘制的内容
,否则得到的结果很可能不是你想要的。我用图片来具体说明一下:
如图所示,由于透明区域过小而覆盖不到的地方,将不会受到 Xfermode 的影响。
二、效果类方法
效果类的 API ,指的就是抗锯齿、填充/轮廓、线条宽度等等这些。
上一节已经讲过的内容这里就不重复了
- setAntiAlias 设置抗锯齿
- setStyle 设置图形是线条风格还是填充风格的。
设置线条形状
设置线条形状的一共有 4 个方法: setStrokeWidth(float width)
, setStrokeCap(Paint.Cap cap)
, setStrokeJoin(Paint.Join join)
, setStrokeMiter(float miter)
。
setStrokeWidth 线条宽度
设置线条宽度。单位为像素,默认值是 0。
线条宽度 0 和 1 的区别
默认情况下,线条宽度为 0,但你会发现,这个时候它依然能够画出线,线条的宽度为 1 像素。那么它和线条宽度为 1 有什么区别呢?
其实这个和后面要讲的一个「几何变换」有关:你可以为 Canvas
设置 Matrix
来实现几何变换(如放大、缩小、平移、旋转),在几何变换之后 Canvas
绘制的内容就会发生相应变化,包括线条也会加粗,例如 2 像素宽度的线条在 Canvas
放大 2 倍后会被以 4 像素宽度来绘制。
而当线条宽度被设置为 0 时,它的宽度就被固定为 1 像素,就算 Canvas
通过几何变换被放大,它也依然会被以 1 像素宽度来绘制。Google 在文档中把线条宽度为 0 时称作「hairline mode(发际线模式)」。
setStrokeCap 线头形状
设置线头的形状。线头形状有三种:BUTT
平头、 ROUND
圆头、 SQUARE
方头。默认为 BUTT
。
当线条的宽度是 1 像素时,这三种线头的表现是完全一致的,全是 1 个像素的点;而当线条变粗的时候,它们就会表现出不同的样子:
虚线是额外加的,虚线左边是线的实际长度,虚线右边是线头。有了虚线作为辅助,可以清楚地看出
BUTT
和SQUARE
的区别。
setStrokeJoin 拐角形状
设置拐角的形状。有三个值可以选择:MITER
尖角(默认)、 BEVEL
平角和 ROUND
圆角。
setStrokeMiter 拐角延长线
这个方法是对于 setStrokeJoin()
的一个补充,它用于设置 MITER
型拐角的延长线的最大值。所谓「延长线的最大值」,是这么一回事:
当线条拐角为 MITER
时,拐角处的外缘需要使用延长线来补偿:
而这种补偿方案会有一个问题:如果拐角的角度太小,就有可能由于出现连接点过长的情况。比如这样:
所以为了避免意料之外的过长的尖角出现, MITER
型连接点有一个额外的规则:当尖角过长时,自动改用 BEVEL
的方式来渲染连接点。例如上图的这个尖角,在默认情况下是不会出现的,而是会由于延长线过长而被转为 BEVEL
型连接点:
至于多尖的角属于过于尖,尖到需要转为使用 BEVEL
来绘制,则是由一个属性控制的,而这个属性就是 setStrokeMiter(miter)
方法中的 miter
参数。miter
参数是对于转角长度的限制,具体来讲,是指尖角的外缘端点和内部拐角的距离与线条宽度的比。也就是下面这两个长度的比:
这个 miter limit 的默认值是 4,对应的是一个大约
29°
的锐角:
默认情况下,大于这个角的尖角会被保留,而小于这个夹角的就会被「削成平头」
所以,这个方法它其实设置的是「 线条在 Join
类型为 MITER
时对于 MITER
的长度限制」。
色彩优化
Paint
的色彩优化有两个方法:setDither(boolean dither)
和 setFilterBitmap(boolean filter)
。它们的作用都是让画面颜色变得更加「顺眼」,但原理和使用场景是不同的。
setDither 设置图像的抖动
设置图像的抖动。注意,它就叫抖动,不是防抖动,也不是去抖动,有些人在翻译的时候自作主张地加了一个「防」字或者「去」字,这是不对的。
所谓抖动是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。
不过对于现在(2017年)而言,
setDither(dither)
已经没有当年那么实用了,因为现在的 Android 版本的绘制,默认的色彩深度已经是 32 位的ARGB_8888
,效果已经足够清晰了。只有当你向自建的Bitmap
中绘制,并且选择 16 位色的ARGB_4444
或者RGB_565
的时候,开启它才会有比较明显的效果。
setFilterBitmap 使用双线性过滤
设置是否使用双线性过滤来绘制 Bitmap
。
图像在放大绘制
的时候,默认使用的是最近邻插值过滤
,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤
,就可以让结果图像显得更加平滑。
效果盗维基百科的图:
setPathEffect 设置轮廓效果
使用 PathEffect
来给图形的轮廓设置效果。对 Canvas
所有的图形绘制有效。
Android 中有 6 种 PathEffect
:
- 单一效果的
CornerPathEffect
DiscretePathEffect
DashPathEffect
PathDashPathEffect
- 组合效果的
SumPathEffect
ComposePathEffect
CornerPathEffect 圆角
把所有拐角变成圆角。
CornerPathEffect(float radius) //圆角的半径
PathEffect pathEffect = new CornerPathEffect(20);
paint.setPathEffect(pathEffect);
canvas.drawPath(path, paint);
DiscretePathEffect 偏离
把线条进行随机的偏离,让轮廓变得乱七八糟。DiscretePathEffect
具体的做法是,把绘制改为使用定长的线段来拼接
,并且在拼接的时候对路径进行随机偏离
。
DiscretePathEffect(float segmentLength, float deviation) //用来拼接的每个线段的长度,偏离量
PathEffect pathEffect = new DiscretePathEffect(20, 5);
paint.setPathEffect(pathEffect);
canvas.drawPath(path, paint);
DashPathEffect 虚线
使用虚线来绘制线条。
DashPathEffect(float[] intervals, float phase) //虚线的格式,偏移量
PathEffect pathEffect = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
paint.setPathEffect(pathEffect);
canvas.drawPath(path, paint);
虚线的格式:
- 数组中元素必须为偶数(最少是 2 个)
- 按照「画线长度、空白长度、画线长度、空白长度」......的顺序排列
- 例如上面代码中的
20, 5, 10, 5
就表示虚线是按照「画 20 像素、空 5 像素、画 10 像素、空 5 像素」的模式来绘制
PathDashPathEffect 图形虚线
这个方法比 DashPathEffect
多一个前缀 Path
,所以顾名思义,它是使用一个 Path
来绘制「虚线」。
PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style)
Path dashPath = ...; // 使用一个三角形来做 dash
PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0, PathDashPathEffectStyle.TRANSLATE);
paint.setPathEffect(pathEffect);
canvas.drawPath(path, paint);
参数:
shape
参数是用来绘制的Path
advance
是两个相邻的shape
段之间的间隔,不过注意,这个间隔是两个shape
段的起点的间隔,而不是前一个的终点和后一个的起点的距离phase
和DashPathEffect
中一样,是虚线的偏移style
,是用来指定拐弯改变的时候shape
的转换方式,类型为PathDashPathEffect.Style
,有三个值:TRANSLATE
:位移ROTATE
:旋转MORPH
:变体
SumPathEffect 组合
这是一个组合效果类的 PathEffect
。它的行为特别简单,就是分别按照两种 PathEffect
分别对目标进行绘制。
PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
pathEffect = new SumPathEffect(dashEffect, discreteEffect);
canvas.drawPath(path, paint);
ComposePathEffect 组合
这也是一个组合效果类的 PathEffect
。不过它是先对目标 Path
使用一个 PathEffect
,然后再对这个改变后的 Path
使用另一个 PathEffect
。
PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
pathEffect = new ComposePathEffect(dashEffect, discreteEffect);
canvas.drawPath(path, paint);
它的构造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe)
中的两个 PathEffect
参数, innerpe
是先应用的, outerpe
是后应用的。所以上面的代码就是「先偏离,再变虚线」。而如果把两个参数调换,就成了「先变虚线,再偏离」。
注意: PathEffect
在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:
Canvas.drawLine()
和Canvas.drawLines()
方法画直线时,setPathEffect()
是不支持硬件加速的;PathDashPathEffect
对硬件加速的支持也有问题,所以当使用PathDashPathEffect
的时候,最好也把硬件加速关了。
setShadowLayer 阴影
在之后的绘制内容下面加一层阴影。
setShadowLayer(float radius, float dx, float dy, int shadowColor) //阴影的模糊范围、偏移量、颜色
paint.setShadowLayer(10, 0, 0, Color.RED);
canvas.drawText(text, 80, 300, paint);
//clearShadowLayer(); //清除阴影层
注意:
- 在硬件加速开启的情况下,
setShadowLayer()
只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影 - 如果
shadowColor
是半透明的,阴影的透明度就使用shadowColor
自己的透明度 - 如果
shadowColor
是不透明的,阴影的透明度就使用paint
的透明度
setMaskFilter 遮罩
为之后的绘制设置 MaskFilter
。上一个方法 setShadowLayer()
是设置的在绘制层下方
的附加效果;而这个 MaskFilter
和它相反,设置的是在绘制层上方
的附加效果。
到现在已经有两个
setXxxFilter(filter)
了。前面有一个setColorFilter(filter)
是对每个像素的颜色进行过滤;而这里的setMaskFilter(filter)
则是基于整个画面来进行过滤。
MaskFilter
有两种: BlurMaskFilter
和 EmbossMaskFilter
。
BlurMaskFilter 模糊效果
模糊效果的 MaskFilter
。
BlurMaskFilter(float radius, BlurMaskFilter.Blur style) //模糊的范围,模糊的类型
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
canvas.drawBitmap(bitmap, 100, 100, paint);
模糊类型一共有四种:
NORMAL
: 内外都模糊绘制SOLID
: 内部正常绘制,外部模糊INNER
: 内部模糊,外部不绘制OUTER
: 内部不绘制,外部模糊(什么鬼?)
EmbossMaskFilter 浮雕效果
浮雕效果的 MaskFilter
。
EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius)
paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));
canvas.drawBitmap(bitmap, 100, 100, paint);
参数:
direction
是一个 3 个元素的数组,指定了光源的方向ambient
是环境光的强度,数值范围是 0 到 1specular
是炫光的系数blurRadius
是应用光线的范围
获取绘制的 Path
这是效果类的最后一组方法,也是效果类唯一的一组 get
方法。
这组方法做的事是:根据 paint
的设置,计算出绘制 Path
或文字时的 实际 Path。
getFillPath 获取实际的 Path
所谓实际 Path
,指的就是 drawPath()
的绘制内容的轮廓,要算上线条宽度
和设置的 PathEffect
。
默认情况下(线条宽度为 0、没有
PathEffect
),原Path
和实际Path
是一样的;而在线条宽度不为 0 (并且模式为STROKE
模式或FLL_AND_STROKE
),或者设置了PathEffect
的时候,实际Path
就和原Path
不一样了。
getFillPath(Path src, Path dst) //原 Path,实际 Path 的保存位置
getTextPath 文字的 Path
文字的绘制,虽然是使用 Canvas.drawText()
方法,但其实在下层,文字信息全是被转化成图形
,对图形进行绘制的。 getTextPath()
方法,获取的就是目标文字所对应的 Path
。这个就是所谓「文字的 Path
」。
getTextPath(String text, int start, int end, float x, float y, Path path)
getTextPath(char[] text, int index, int count, float x, float y, Path path)
这两个方法主要是用于图形和文字的装饰效果
的位置计算
,比如 自定义的下划线效果:
三、文字绘制相关方法
Paint
有些设置是文字绘制相关的,即和 drawText()
相关的,比如设置文字大小、设置文字间隔、设置各种文字效果。
除此之外, Paint
有超过一半的方法是与文字绘制相关的设置或计算
的方法,非常详细,所以这一节就不讲它们了,把它们放在下一节里单独讲。
四、初始化方法
这一类方法很简单,它们是用来初始化 Paint
对象,或者是批量设置 Paint
的多个属性的方法。
reset 重置
重置 Paint
的所有属性为默认值。相当于重新 new
一个,不过性能当然高一些啦。
set 复制
set(Paint src)
用于把 src
的所有属性全部复制过来。相当于调用 src
所有的 get
方法,然后调用这个 Paint
的对应的 set
方法来设置它们。
setFlags 批量设置
setFlags(int flags)
用于批量设置 flags。相当于依次调用它们的 set
方法。对应的 get
方法是 int getFlags()
。
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
//等价与下面两行代码
paint.setAntiAlias(true);
paint.setDither(true);
最后再强调一遍:这期的内容没必要全部背会,只要看懂、理解,记住有这么个东西就行了。以后在用到的时候,再拐回来翻一翻就行了。
2021-4-23