Paint 详解
Paint 的 API 大致可以分为 4 类:
- 颜色
- 效果
- drawText() 相关
- 初始化
1 颜色
Canvas 绘制的内容,有三层对颜色的处理:
1.1 基本颜色
- 像素的基本颜色,根据绘制内容的不同而有不同的控制方式: Canvas 的颜色填充类方法 drawColor/RGB/ARGB() 的颜色,是直接写在方法的参数里,通过参数来设置的;
- drawBitmap() 的颜色,是直接由 Bitmap 对象来提供的;
- 图形和文字的绘制,它们的颜色就需要使用 paint 参数来额外设置(drawCircle() / drawPath() / drawText() ...)。
Paint 设置颜色的方法有两种:
- 一种是直接用 Paint.setColor/ARGB() 来设置颜色;
- 另一种是使用 Shader 来指定着色方案。
1.1.1 直接设置颜色
1.1.1.1 setColor(int color)
paint.setColor(Color.parseColor("#000000"))
canvas.drawRect(left, top, right, bottom, paint)
paint.setColor(Color.parseColor("#90909009"))
canvas.drawLine(left, top, right, bottom, paint)
paint.setColor(Color.parseColor("#567898"))
canvas.drawText("HenCoder", 100, 100, paint)
1.1.1.2 setARGB(int a, int r, int g, int b)
参数用的是更直接的三原色与透明度的值
paint.setARGB(a, r, g, b)
canvas.drawRect(left, top, right, bottom, paint)
paint.setARGB(a, r, g, b)
canvas.drawLine(left, top, right, bottom, paint)
1.1.2 setShader(Shader shader) 设置 Shader
Shader 着色器,也是用于设置绘制颜色的。着色器设置的是一个颜色方案,或者说是一套着色规则。当设置了 Shader 之后,Paint 在绘制图形和文字时就不使用 setColor/ARGB() 设置的颜色了,而是使用 Shader 的方案中的颜色。
在 Android 的绘制里使用 Shader ,并不直接用 Shader 这个类,而是用它的几个子类。具体来讲有 LinearGradient RadialGradient SweepGradient BitmapShader ComposeShader :
1.1.2.1 LinearGradient 线性渐变
val shader = LinearGradient(x0, y0, x1, y1, Color.parseColor("#E91E63"),Color.parseColor("#123456"), Shader.TileMode.CLAMP)
paint.setShader(shader)
canvas.drawCircle(cx, cy, radius, paint)
置两个点和两种颜色,以这两个点作为端点,使用两种颜色的渐变来绘制颜色。
构造方法:
- LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) 。
参数:
- x0 y0 x1 y1:渐变的两个端点的位置
- color0 color1 是端点的颜色tile:端点范围之外的着色规则,类型是 TileMode。
TileMode 一共有 3 个值可选: CLAMP, MIRROR 和 REPEAT。
- CLAMP 会在端点之外延续端点处的颜色;
- MIRROR 是镜像模式;
- REPEAT 是重复模式。
1.1.2.2 RadialGradient 辐射渐变
辐射渐变就是从中心向周围辐射状的渐变。
val shader = RadialGradient(cx, cy, radius, Color.parseColor("#111111"), Color.parseColor("#2222222"), Shader.TileMode.CLAMP)
paint.setShader(shader)
canvas.drawCircle(cx, cy, radius, paint)
构造方法:
- RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)。
参数:
- centerX centerY:辐射中心的坐标
- radius:辐射半径
- centerColor:辐射中心的颜色
- edgeColor:辐射边缘的颜色
- tileMode:辐射范围之外的着色模式。
1.1.2.3 SweepGradient 扫描渐变
val shader = SweepGradient(cx, cy, Color.parseColor("#33333"),Color.parseColor("#444444"))
paint.setShader(shader)
canvas.drawCircle(cx, cy, radius, paint)
构造方法:
- SweepGradient(float cx, float cy, int color0, int color1)
参数:
- cx cy :扫描的中心
- color0:扫描的起始颜色
- color1:扫描的终止颜色
1.1.2.4 BitmapShader
用 Bitmap 的像素来作为图形或文字的填充
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.batman)
val shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.setShader(shader)
canvas.drawCircle(cx, cy, radius, paint)
构造方法:
- BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
参数:
- bitmap:用来做模板的 Bitmap 对象
- tileX:横向的 TileMode
- tileY:纵向的 TileMode。
1.1.2.5 ComposeShader 混合着色器
所谓混合,就是把两个 Shader 一起使用。
// 第一个 Shader:头像的 Bitmap
val bitmap1 = BitmapFactory.decodeResource(resources, R.drawable.batman)
val shader1 = BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
// 第二个 Shader:从上到下的线性渐变(由透明到黑色)
val bitmap2 = BitmapFactory.decodeResource(resources, R.drawable.batman_logo)
val shader2 = BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
// ComposeShader:结合两个 Shader
val shader = ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER)
paint.setShader(shader)
canvas.drawCircle(cx, cy, radius, paint)
上面这段代码中两个 BitmapShader 来作为 ComposeShader() 的参数,而 ComposeShader() 在硬件加速下是不支持两个相同类型的 Shader 的,所以这里也需要关闭硬件加速才能看到效果。
构造方法:
- ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
参数:
- shaderA, shaderB:两个相继使用的
- Shadermode: 两个 Shader 的叠加模式,即 shaderA 和 shaderB 应该怎样共同绘制。它的类型是 PorterDuff.Mode 。
PorterDuff.Mode
PorterDuff.Mode 一共有 17 个,可以分为两类:
- Alpha 合成 (Alpha Compositing)
- 混合 (Blending)
第一类,Alpha 合成,其实就是 「PorterDuff」 这个词所指代的算法。 「PorterDuff」 并不是一个具有实际意义的词组,而是两个人的名字(准确讲是姓)。这两个人当年共同发表了一篇论文,描述了 12 种将两个图像共同绘制的操作(即算法)。而这篇论文所论述的操作,都是关于 Alpha 通道(也就是我们通俗理解的「透明度」)的计算的,后来人们就把这类计算称为Alpha 合成 ( Alpha Compositing ) 。
看下效果吧。效果直接盗 Google 的官方文档吧。
源图像和目标图像:
![RIE{0$QUU]EJ~]3F5OE8CE.png
Alpha 合成:
![WBOFB_3R)MM~M78{LQLDFO.png
第二类,混合,也就是 Photoshop 等制图软件里都有的那些混合模式(multiply
darken
lighten
之类的)。这一类操作的是颜色本身而不是 Alpha
通道,并不属于 Alpha
合成,所以和 Porter 与 Duff 这两个人也没什么关系,不过为了使用的方便,它们同样也被 Google 加进了 PorterDuff.Mode
里。
官方文档![C35C}H87(IA@E~L8M@ZCH8.png。
结论
从效果图可以看出,Alpha 合成类的效果都比较直观,基本上可以使用简单的口头表达来描述它们的算法(起码对于不透明的源图像和目标图像来说是可以的),例如 SRC_OVER 表示「二者都绘制,但要源图像放在目标图像的上面」,DST_IN 表示「只绘制目标图像,并且只绘制它和源图像重合的区域」。
而混合类的效果就相对抽象一些,只从效果图不太能看得出它们的着色算法,更看不出来它们有什么用。
所以对于这些 Mode,正确的做法是:对于 Alpha 合成类的操作,掌握他们,并在实际开发中灵活运用。
好了,这些就是几个 Shader 的具体介绍。
setShadowLayer(float radius, float dx, float dy, int shadowColor)
在之后的绘制内容下面加一层阴影。
paint.setShadowLayer(10, 0, 0, Color.RED)
canvas.drawText(text, 80, 300, paint);
方法的参数里, radius 是阴影的模糊范围; dx dy 是阴影的偏移量; shadowColor 是阴影的颜色。
如果要清除阴影层,使用 clearShadowLayer() 。
注意:
在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。
如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。
setMaskFilter(MaskFilter maskfilter)
为之后的绘制设置 MaskFilter。上一个方法 setShadowLayer() 是设置的在绘制层下方的附加效果;而这个 MaskFilter 和它相反,设置的是在绘制层上方的附加效果。
到现在已经有两个 setXxxFilter(filter) 了。前面有一个 setColorFilter(filter) ,是对每个像素的颜色进行过滤;而这里的 setMaskFilter(filter) 则是基于整个画面来进行过滤。
MaskFilter 有两种:
- BlurMaskFilter
- EmbossMaskFilter。
BlurMaskFilter
模糊效果的 MaskFilter。
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL))
canvas.drawBitmap(bitmap, 100, 100, paint)
它的构造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 参数是模糊的范围, style 是模糊的类型。一共有四种:
- NORMAL: 内外都模糊绘制
- SOLID: 内部正常绘制,外部模糊
- INNER: 内部模糊,外部不绘制
- OUTER: 内部不绘制,外部模糊(什么鬼?)
EmbossMaskFilter
雕效果的 MaskFilter。
paint.setMaskFilter( EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10))
canvas.drawBitmap(bitmap, 100, 100, paint)
它的构造方法 EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的参数里, direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。