第3章 画笔Paint详解:颜色与样式设置、抗锯齿与抖动、StrokeCap与StrokeJoin、Shader与颜色渐变
各位同学,欢迎来到第三章。上一章我们把Canvas的基本操作捋了一遍,今天来聊聊Paint——这个在自定义View里出镜率最高的对象。
说实话,我刚接触Android绘图那会儿,总觉得Paint不就是个画笔画笔嘛,设个颜色、设个粗细就完事了。后来才发现,这里面的门道真不少。你想想看,同样一个圆,用不同的Paint配置画出来,效果天差地别。
3.1 颜色与样式设置
Paint最基础的能力,就是设置颜色和填充样式。咱们先看最常用的几个方法:
val paint = Paint()
paint.color = Color.RED // 设置颜色
paint.alpha = 128 // 设置透明度 0-255
paint.style = Paint.Style.FILL // 填充模式
这里有个细节我特别想强调——color和alpha的关系。很多人以为设置color的时候就能把透明度带进去,比如Color.parseColor("#80FF0000")。但如果你后面又调用了paint.alpha = 255,那之前设的透明度就被覆盖了。
我在项目中遇到过这个坑:画一个半透明的遮罩层,明明颜色值里带了透明度,画出来却是实心的。排查了半天,发现是某处代码顺手设了个alpha(255)。嗯,这个顺序问题,大家一定要留意。
样式设置就三种:
| 样式 | 说明 | 效果 |
|---|---|---|
| FILL | 填充内部 | 只填充,不描边 |
| STROKE | 仅描边 | 只画轮廓,内部透明 |
| FILL_AND_STROKE | 填充+描边 | 既填充又画轮廓 |
3.2 抗锯齿与抖动
抗锯齿,英文叫Anti-Alias。说白了就是让图形的边缘看起来更平滑,不那么「狗啃」。
paint.isAntiAlias = true // 开启抗锯齿
paint.isDither = true // 开启抖动
这两个开关,我建议你默认都打开。为什么?
抗锯齿的原理是在边缘像素上做半透明过渡。比如一条斜线,如果不抗锯齿,边缘就是一个个锯齿状的方块。开了抗锯齿,边缘的像素会变成半透明,视觉上就平滑了。
抖动呢?它解决的是颜色深度的问题。在低色深设备上,颜色不够用,抖动通过像素点的排列组合,模拟出更多颜色。现在主流设备都是32位色深,抖动的效果不那么明显了。但我个人习惯还是开着,反正性能开销极小,有备无患。
3.3 StrokeCap与StrokeJoin
这两个属性,专门管线条的两头和拐角长什么样。
StrokeCap:线的帽子
有三种取值:
paint.strokeCap = Paint.Cap.BUTT // 平头,不延伸
paint.strokeCap = Paint.Cap.ROUND // 圆头,半圆延伸
paint.strokeCap = Paint.Cap.SQUARE // 方头,矩形延伸
BUTT和SQUARE的区别在哪?我刚开始也搞混过。你看:BUTT就是线画到哪就停在哪,端面是平的。SQUARE呢,它会在端点的基础上再延伸出半个线宽的长度,端面也是平的。所以SQUARE比BUTT长出一截。
ROUND最好理解,就是给线头戴个半圆形的帽子。
StrokeJoin:线的关节
paint.strokeJoin = Paint.Join.MITER // 尖角
paint.strokeJoin = Paint.Join.ROUND // 圆角
paint.strokeJoin = Paint.Join.BEVEL // 平角
MITER是默认值,两条线相交的地方会形成一个尖角。但如果角度太小,尖角会伸出去很长,这时候可以用strokeMiter来限制。
ROUND就是圆润的过渡,BEVEL则是把尖角切掉,形成一个斜面。
实战经验:画折线图的时候,我建议用ROUND的Join和Cap。这样线条看起来更流畅、更现代。如果你画的是建筑图纸或者UI边框,MITER更合适,显得硬朗、精确。
3.4 Shader与颜色渐变
好了,终于到了Shader。这是Paint里最有意思的部分之一。
Shader说白了就是「着色器」。它告诉Paint:别用单一颜色了,用这个渐变或者图片来填充吧。
Android提供了几种内置Shader:
- LinearGradient:线性渐变
- RadialGradient:径向渐变
- SweepGradient:扫描渐变
- BitmapShader:图片着色
- ComposeShader:组合着色
LinearGradient 线性渐变
最常用的渐变方式。从点A到点B,颜色逐渐变化。
val shader = LinearGradient(
0f, 0f, // 起点x, y
width.toFloat(), 0f, // 终点x, y
intArrayOf(Color.RED, Color.BLUE, Color.GREEN), // 颜色数组
floatArrayOf(0f, 0.5f, 1f), // 位置数组,对应颜色的位置
Shader.TileMode.CLAMP // 平铺模式
)
paint.shader = shader
这里有个参数叫TileMode,它决定了渐变范围之外的区域怎么处理:
| 模式 | 行为 |
|---|---|
| CLAMP | 边缘颜色向外延伸 |
| REPEAT | 重复渐变 |
| MIRROR | 镜像重复 |
RadialGradient 径向渐变
从一个中心点向外辐射的渐变。适合做光晕、聚光灯效果。
val shader = RadialGradient(
centerX, centerY, // 圆心
radius, // 半径
Color.WHITE, // 中心颜色
Color.TRANSPARENT, // 边缘颜色
Shader.TileMode.CLAMP
)
SweepGradient 扫描渐变
这个比较特殊,它是围绕中心点旋转的渐变。做仪表盘、色轮的时候特别好用。
val shader = SweepGradient(
centerX, centerY,
intArrayOf(Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED),
null // 传null表示均匀分布
)
BitmapShader 图片着色
用图片来填充图形。比如你想画一个圆形头像,就可以用这个。
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.avatar)
val shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.shader = shader
// 然后画一个圆,图片就会以圆形裁剪显示
canvas.drawCircle(centerX, centerY, radius, paint)
ComposeShader 组合着色
把两个Shader组合起来,通过Xfermode混合。
val shader1 = LinearGradient(...)
val shader2 = BitmapShader(...)
val composeShader = ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER)
paint.shader = composeShader
这个玩法就多了。你可以做出纹理叠加渐变、图片叠加渐变等各种效果。
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)。
小结
这一章我们聊了Paint的核心配置。从颜色样式到抗锯齿,从线条端点到渐变着色。这些东西单独看都不难,但组合起来就能画出千变万化的效果。
我个人建议你动手试试:创建一个自定义View,用不同的Shader画同一个图形,看看效果差异。尤其是LinearGradient和RadialGradient,在实际项目中用的频率非常高。
下一章,我们会深入Path——那个能画出任意形状的利器。到时候你会发现,配合今天学的Shader,能玩出更多花样。
好,今天就到这里。有问题欢迎交流。
公众号:蓝海资料掘金营,微信deep3321