Canvas绘图之Shader使用图文详解
[转载 http://blog.csdn.net/iispring/article/details/50500106]
我们在用Android中的Canvas绘制各种图形时,可以通过Paint.setShader(shader)方法为画笔Paint设置shader,这样就可以绘制出多彩的图形。那么Shader是什么呢?做过GPU绘图的同学应该都知道这个词汇,Shader就是着色器的意思。我们可以这样理解,Canvas中的各种drawXXX方法定义了图形的形状,画笔中的Shader则定义了图形的着色、外观,二者结合到一起就决定了最终Canvas绘制的被色彩填充的图形的样子。
类android.graphics.Shader有五个子类,分别是:BitmapShader、LinearGradient、RadialGradient、SweepGradient和ComposeShader,下面依次对这几个类的使用分别说明。
BitmapShader
BitmapShader,顾名思义,就是用Bitmap对绘制的图形进行渲染着色,其实就是用图片对图形进行贴图。
BitmapShader构造函数如下所示:
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
第一个参数是Bitmap对象,该Bitmap决定了用什么图片对绘制的图形进行贴图。
第二个参数和第三个参数都是Shader.TileMode类型的枚举值,有以下三个取值:CLAMP 、REPEAT 和 MIRROR。
- CLAMP
CLAMP表示,当所画图形的尺寸大于Bitmap的尺寸的时候,会用Bitmap四边的颜色填充剩余空间。
注意,我们这张图片的四个角是有一定的圆弧的,也就是该Bitmap的四个角点处的像素都是透明的。
我们使用该Bitmap,演示TileMode为CLAMP的效果,代码如下所示:
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
canvas.drawRect(0, 0, bitmap.getWidth() * 2, bitmap.getHeight() * 2, paint);
效果如下所示:
这里写图片描述
我们可以看到,由于我们所绘制的矩形矩形区域比Bitmap大,Bitmap就用右侧边和下侧边的最外层的颜色填充了矩形区域。由于原Bitmap右下角的像素是透明的,所以绘制的矩形的右下角就用透明填充了。
如果我们绘制的图形尺寸小于Bitmap尺寸,那么效果看起来就像是对原Bitmap裁剪了一下而已,代码如下所示:
这里写图片描述
我们可以看到,当我们所绘制的圆形尺寸小于Bitmap尺寸的时候,看起来的效果就是我们用所绘制的圆形对Bitmap进行了裁剪。
REPEAT
REPEAT表示,当我们绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域。
示例代码如下所示:
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
paint.setShader(bitmapShader);
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
效果如下所示:
这里写图片描述
MIRROR
与REPEAT类似,当绘制的图形尺寸大于Bitmap尺寸时,MIRROR也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是,两个相邻的Bitmap互为镜像。
代码如下所示:
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
paint.setShader(bitmapShader);
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
效果如下所示:
这里写图片描述
最后需要说的是,在构造BitmapShader时,tileX和tileY可以取不同的值,二者不用非得一致。
LinearGradient
我们可以用LinearGradient创建线性渐变效果,其有两个构造函数:
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
我们重点说一下第一个构造函数,在此基础上理解第二个构造函数就很简单了。
LinearGradient是用来创建线性渐变效果的,它是沿着某条直线的方向渐变的,坐标(x0,y0)就是这条渐变直线的起点,坐标(x1,y1)就是这条渐变直线的终点。需要说明的是,坐标(x0,y0)和坐标(x1,y1)都是Canvas绘图坐标系中的坐标。color0和color1分别表示了渐变的起始颜色和终止颜色。与BitmapShader类似,LinearGradient也支持TileMode,有以下三个取值:CLAMP 、REPEAT 和 MIRROR。
使用代码如下所示:
LinearGradient linearGradient = new LinearGradient(100, 100, 500, 500, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
canvas.drawRect(100, 100, 500, 500, paint);
效果如下所示:
这里写图片描述
上面我们使用了CLAMP,但是由于我们绘制的矩形与渐变位置的大小一样大,所以CLAMP效果不明显。
我们把绘制的区域变大,还是用CLAMP,这次绘制整个Canvas大小的矩形。
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
效果如下所示:
这里写图片描述
当我们把CLAMP改为REPEAT时,还是绘制整个Canvas大小的矩形,效果如下所示:
这里写图片描述
当我们用MIRROR绘制整个Canvas大小的矩形的时候,效果如下所示:
这里写图片描述
在LinearGradient的第二个构造函数中可以通过参数colors传入多个颜色值进去,这样就会用colors数组中指定的颜色值一起进行颜色线性插值。还可以指定positions数组,该数组中每一个position对应colors数组中每个颜色在线段中的相对位置,position取值范围为[0,1],0表示起始位置,1表示终止位置。如果positions数组为null,那么Android会自动为colors设置等间距的位置。
RadialGradient
我们可以用RadialGradient创建从中心向四周发散的辐射渐变效果,其有两个构造函数:
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
这两个构造函数和LinearGradient的两个构造函数很类似,我们此处还是重点讲解第一个构造函数,在此基础上理解第二个构造函数就很简单了。
RadialGradient是用来创建从中心向四周发散的辐射渐变效果的,所以我们需要在其构造函数中传入一些圆的参数,坐标(centerX,centerY)是圆心,即起始的中心颜色的位置,radius确定了圆的半径,在圆的半径处的颜色是edgeColor,这样就确定了当位置从圆心移向圆的轮廓时,颜色逐渐从centerColor渐变到edgeColor。RadialGradient也支持TileMode参数,有以下三个取值:CLAMP 、REPEAT 和 MIRROR。
我们首先将CLAMP作为TileMode,代码如下所示:
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
float centerX = canvasWidth / 2f;
float centerY = canvasHeight / 2f;
float radius = canvasWidth / 4f;
RadialGradient radialGradient = new RadialGradient(centerX, centerY, radius, Color.GREEN, Color.BLUE, Shader.TileMode.MIRROR);
paint.setShader(radialGradient);
canvas.drawRect(0, 0, canvasWidth, canvasHeight, paint);
效果如下所示:
这里写图片描述
在上图中,我们绘制的矩形和Canvas大小一样大,其尺寸大于我们定义的RadialGradient的圆的尺寸。我们可以看到,当使用CLAMP作为TileMode时,颜色从圆心的绿色向圆周的蓝色渐变,在圆以外的空间都用edgeColor蓝色填充。
当我们把CLAMP改为REPEAT时,还是画同样的矩形,效果如下所示:
这里写图片描述
我们看到,颜色以绿色到蓝色作为一个渐变周期从圆心向外扩散。
当我们使用MIRROR作为TileMode时,还是画同样的矩形,效果如下所示:
这里写图片描述
我们看到,颜色以绿色->蓝色->绿色->蓝色…周期性地交替变换从圆心向外扩散。
在RadialGradient的第二个构造函数中可以通过参数colors传入多个颜色值进去,这样就会用colors数组中指定的颜色值一起进行颜色线性插值。还可以指定stops数组,该数组中每一个stop对应colors数组中每个颜色在半径中的相对位置,stop取值范围为[0,1],0表示圆心位置,1表示圆周位置。如果stops数组为null,那么Android会自动为colors设置等间距的位置。
SweepGradient