自定义View之Paint - BitmapShader

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系列文章目录




如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!


版权声明:本文为IO_Field原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。