Shader
在Canvas绘制各种图形时,可以调用Paint.setShader(shader)方法来为Paint设置Shader,以绘制七彩缤纷的图形。Shader又是什么呢?Shader被称为着色器,它就像绘画使用的调料盘,放置了各色的颜料。在Canvas绘制任何图形时,Paint会从Shader中获取其颜色。
在官方文档中,从API 26开始,其构造方法已过期,不再推荐使用,而是由其子类代替。
Shader共有5个子类:BitmapShader、LinearGradient、RadialGradient、SweepGradient和ComposeShader。
BitmapShader
BitmapShader就是将位图作为纹理,以平铺的模式填充图形。位图可以设置平铺的模式 - 镜像或者重复。
BitmapShader的构造函数:
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
- bitmap: BitMap实例,它决定了以哪个BitMap作为着色器。
- tileX: 决定了X轴方向上以哪种模式来平铺。
- tileY:决定了Y轴方向上以哪种模式来平铺。
先来看原图,原图中有只小猴子:
如果我们想做到下图的效果:
有人可能会说,只要绘制多个原图就可以了。实际上,并不需要这么麻烦,我们只要合理的使用Shader.TileMode即可。Shader.TileMode不会什么魔法,它只是一个枚举类,定义了CLAMP、REPEAT、MIRROR三个值。
public enum TileMode {
CLAMP (0),
REPEAT (1),
MIRROR (2);
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
TileMode.CLAMP
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_mokey_180)
val bitMapShader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
mPaint.shader = bitMapShader
canvas?.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), mPaint)
}
效果图:
从效果图和原图相比,我们重点关注超过BitMap尺寸的部分,它们是以什么颜色来填充的?现在我们来看下TileMode.CLAMP的定义:
TileMode.CLAMP:如果绘制的图形的尺寸大于BitMap的尺寸时,超出的部分将会使用BitMap周边的颜色来填充图形。
从效果图上,可以清晰的看出,如果纹理本身小于绘制的面积,超出的部分以纹理的边缘颜色进行填充,所以造成了上述的绘制效果。
TileMode.REPEAT
TileMode.REPEAT: 如果绘制的图形的尺寸大于BitMap的尺寸时,如果绘制的图形的尺寸大于BitMap的尺寸时,将重复着着色器的BitMap(水平和垂直)填充图形.
当我们把x方向的平铺方式设置为TileMode.MIRROR时:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_mokey_180)
val bitMapShader = BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP)
mPaint.shader = bitMapShader
canvas?.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), mPaint)
}
效果图:
TileMode.MIRROR
TileMode.MIRROR: 如果绘制的图形的尺寸大于BitMap的尺寸时,与REPEAT类似,它也会重复着着色器的BitMap(水平和垂直)以填充图形,不同的是,两个相邻的BitMap互为镜像
当我们把x方向的平铺方式设置为TileMode.MIRROR时:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_mokey_180)
val bitMapShader = BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.CLAMP)
mPaint.shader = bitMapShader
canvas?.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), mPaint)
}
效果图:
混合模式
前面的示例中,我们只是对x方向的纹理平铺方式进行设置,了解到:
- CLAMP:如果绘制的图形的尺寸大于BitMap的尺寸时,超出的部分将会使用BitMap周边的颜色来填充图形
- REPEAT:如果绘制的图形的尺寸大于BitMap的尺寸时,将重复着着色器的BitMap(水平和垂直)填充图形
- MIRROR:如果绘制的图形的尺寸大于BitMap的尺寸时,与REPEAT类似,它也会重复着着色器的BitMap(水平和垂直)以填充图形,不同的是,两个相邻的BitMap互为镜像
当平铺模式为TileMode.REPEAT时,x方向与我们想要的效果是一致的,y方向并不能满足我们的需要。我们可以看到y方向是用原图的下边的颜色拉伸来填充了,而其平铺模式为Shader.TileMode.CLAMP:
这样也就是说,我们只需要将y方向的屏幕模式设置为Shader.TileMode.REPEAT,应该就可以达到我们想要的效果:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_mokey_180)
val bitMapShader = BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
mPaint.shader = bitMapShader
canvas?.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), mPaint)
}
效果图:
若想了解更多Paint相关的内容,请跳入: 自定义View系列文章目录
如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!