在上篇说道BitmapShader的使用
关于Shader.TileMode这个参数在说明一下
Shader.TileMode.CLAMP:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形的四周颜色进行填充
效果:填充颜色根据边缘填充
Shader.TileMode.REPEAT:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形重复填充
Shader.TileMode.MIRROR:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形镜像填充
说明一下这里的 Shader.TileMode 可以设置不同
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
shader有5个子类
LinearGradient使用
float x0, float y0起始xy
float x1, float y1结束xy
color0起始颜色
color1结束颜色
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
两个构造方法 一个颜色一个颜色数组
LinearGradient linearGradient = new LinearGradient(0,0,200,200, Color.BLUE,Color.RED,Shader.TileMode.CLAMP);
直接上三种效果
RadialGradient使用
centerX,centerY其实中心xy
radius半径
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)
RadialGradient radialGradient = new RadialGradient(canvas.getWidth()/2f,canvas.getHeight()/2f,canvas.getWidth()/4f,Color.WHITE,Color.GRAY,Shader.TileMode.MIRROR);
SweepGradient使用
cx,cy 中心坐标
SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient(float cx, float cy, int[] colors, float[] positions)
//颜色组
int[] colors = {Color.WHITE, Color.GRAY,Color.BLUE};
//按顺时针3点钟为0f,假如我们实现下半年部分180°为白色,左上90°为灰色,右上90°为蓝色
float[] positions = {0.5f, 0.75f,1f};
//positions 如果null则颜色均匀分布
SweepGradient sweepGradient =
new SweepGradient(canvas.getWidth() / 2f, canvas.getHeight() / 2f, colors, positions );
这里我遇到一个问题一直困惑根据api了解
* @param positions May be NULL. The relative position of
* each corresponding color in the colors array, beginning
* with 0 and ending with 1.0. If the values are not
* monotonic, the drawing may produce unexpected results.
* If positions is NULL, then the colors are automatically
* spaced evenly.
*/
public SweepGradient(float cx, float cy,
int colors[], float positions[])
positions最大1.0,我在其他博客中看到如果设置不到小于1f,可显示为带有缺口的图形,如果画一个园设置小雨1.0f的数值 可显示扇形
我试了,发现不行.float[] positions = {0.25f, 0.5f,0.75f};
ComposeShader
ComposeShader,顾名思义,就是混合Shader的意思,它可以将两个Shader按照一定的Xfermode组合起来。
ComposeShader有两个构造函数,如下所示:
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
如果对Xfermode不熟悉的话,强烈建议您先读一下我的另一篇博文《Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解》。
此处对Xfermode做一下简单介绍,Xfermode可以用于实现新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合。Xfermode有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,其中前两个类现在被Android废弃了,现在主要用的是PorterDuffXfermode。PorterDuffXfermode的构造函数需要指定PorterDuff.Mode的类型。所以,上面的第二个构造函数可以看做是第一个构造函数的特例。我们主要讲解第二个,二者大同小异。
我们知道,在使用Xfermode的时候,存在目标像素DST和源像素SRC之说。源像素指的是将要向Canvas上绘制的像素,目标像素指的是源像素在Canvas上对应位置已经存在的像素。
构造函数中的shaderA对应着目标像素,shaderB对应着源像素。
有一点需要说明,ComposeShader这个类不是必须的,也就是我们不用这个类也能创造对应的效果,它类似于一个助手类,为我们实现某种效果提供了方便,下面举例说明。
我们有如下透明图片:
上面的图片是透明的,不过图片中有个心形图案是白色,不透明。 我想让渐变颜色只填充上图中的❤形区域,透明部分不填充,颜色从绿色渐变到蓝色,渐变方向从左上角到右下角。我们不用ComposeShader即可实现上述效果,代码如下所示:
int bitmapWidth = bitmap.getWidth();int bitmapHeight = bitmap.getHeight();//将绘制代码放入到canvas.saveLayer()和canvas.restore()之间canvas.saveLayer(0, 0, bitmapWidth, bitmapHeight, null, Canvas.ALL_SAVE_FLAG); //创建BitmapShader,用以绘制❤形 BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); //将BitmapShader作为画笔paint绘图所使用的shader paint.setShader(bitmapShader); //用BitmapShader绘制矩形 canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint); //将画笔的Xfermode设置为PorterDuff.Mode.MULTIPLY模式 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); //创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果 LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP); //将创建LinearGradient作为画笔paint绘图所使用的shader paint.setShader(linearGradient); //用LinearGradient绘制矩形 canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint); //最后将画笔去除掉Xfermode paint.setXfermode(null);canvas.restore();
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//将绘制代码放入到canvas.saveLayer()和canvas.restore()之间
canvas.saveLayer(0, 0, bitmapWidth, bitmapHeight, null, Canvas.ALL_SAVE_FLAG);
//创建BitmapShader,用以绘制❤形
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//将BitmapShader作为画笔paint绘图所使用的shader
paint.setShader(bitmapShader);
//用BitmapShader绘制矩形
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
//将画笔的Xfermode设置为PorterDuff.Mode.MULTIPLY模式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
//创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果
LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
//将创建LinearGradient作为画笔paint绘图所使用的shader
paint.setShader(linearGradient);
//用LinearGradient绘制矩形
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
//最后将画笔去除掉Xfermode
paint.setXfermode(null);
canvas.restore();
效果如下所示:
如果认真读过博文《Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解》的话,我相信大家应该能明白上图出现的原因。
此处我们还是一起分析一下代码的执行过程。
我们的图片中间的❤形区域是纯白色,该区域的像素颜色值ARGB分量是(255,255,255,255)。❤形区域以外的区域是纯透明的,该区域的像素颜色值ARGB分量是(0,0,0,0)。
为了使用Xfermode,我们将绘图的代码放到了canvas.saveLayer()和canvas.restore()之间,对此有疑问的同学可以参见我上述提到的博文。canvas.saveLayer()会创建一个新的绘图图层,而且该图层是全透明的,我们后面的代码都是绘制到这个图层上,而不是直接绘制到Canvas上。
我们用上述Bitmap创建了一个BitmapShader,并将其绑定到画笔Paint中。当我们用canvas.drawRect()绘制矩形时,就会用该BitmapShader填充,此时的效果应该是在新创建的layer上绘制了一个白色的心形。
然后我们创建了一个PorterDuffXfermode的实例,并通过paint.setXfermode()将其绑定到画笔paint上。其中PorterDuffXfermode的mode类型为MULTIPLY。MULTIPLY的意思是将源像素的ARGB四个分量分别与目标像素对应的ARGB四个分量相乘,将相乘的结果作为混合后的像素。此处进行相乘时,ARGB四个分量都已经从[0, 255]的区间归一化到[0.0, 1.0]的区间。
然后我们创建了一个LinearGradient,用以实现颜色线性渐变效果。颜色从左上角的绿色渐变到右下角的蓝色。然后我们通过paint.setShader()方法将其绑定到画笔paint的shader上。
后面我们再次调用canvas.drawRect()绘制同样大小的一个矩形。在绘制时,我们的画笔已经同时绑定了Xfermode和Shader。首先canvas会用LinearGradient绘制一个具有渐变色的矩形区域。然后根据画笔设置的PorterDuff.Mode.MULTIPLY类型,将那些由渐变色填充的矩形区域中的像素与我们在第3步中绘制的心形图片中的像素颜色进行相乘混合。渐变色填充的矩形区域中的像素是源像素,第3步中绘制的心形图片中的像素是目标像素。目标像素中❤形区域是纯白色的,其像素颜色是(255,255,255,255),归一化后的颜色是(1,1,1,1),对应位置的源像素中的ARGB颜色分量与其相乘,最终的颜色还是源像素的颜色,即心形区域被源像素着上了渐变色。目标像素中❤形区域以外的颜色是纯透明的,颜色是(0,0,0,0),对应位置的源像素中的ARGB颜色分量与其相乘,最终的颜色还是目标像素中的(0,0,0,0),即心形区域以外没有被着色,依旧呈现透明色。
最后通过调用canvas.restore()方法将新创建的layer绘制到Canvas上去,这样我们就看到最终的效果了。
下面我们看看如和用ComposeShader实现上述效果,代码如下所示:
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//创建BitmapShader,用以绘制❤形
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果
LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
//bitmapShader对应目标像素,linearGradient对应源像素,像素颜色混合采用MULTIPLY模式
ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
//将组合的composeShader作为画笔paint绘图所使用的shader
paint.setShader(composeShader);
//用composeShader绘制矩形区域
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
用ComposeShader实现的效果与上图相同,我就不再贴图了。我们可以看到,使用ComposeShader之后,实现相同的效果时,代码量明显减少了,而且我们也不需要将绘图代码放到canvas.saveLayer()和canvas.restore()之间了。
根据上面的示例,我们可以得出如下结论: 假设我们定义了两个Shader的变量,shaderA和shaderB,并分别对这两个Shader进行了实例化。 可以使用ComposeShader将二者组合使用,基本代码如下所示:
ComposeShader composeShader = new ComposeShader(shaderA, shaderB, porterDuffMode);
paint.setShader(composeShader);
canvas.drawXXX(..., paint);
上述代码等价于下面的代码片段:
canvas.saveLayer(left, top, right, bottom, null, Canvas.ALL_SAVE_FLAG);
paint.setShader(shaderA);
canvas.drawXXX(..., paint);
paint.setXfermode(new PorterDuffXfermode(mode));
paint.setShader(shaderB);
canvas.drawXXX(..., paint);
paint.setXfermode(null);
canvas.restore();
此处所说的以上两个代码片段等价的前提是,两个代码片段中的canvas.drawXXX(…, paint)方法中调用的drawXXX方法相同,并且里面传入的参数都相同,例如我们之前两段心形代码示例中都调用drawRect()方法且绘制的矩形的位置及尺寸都相同。
ComposeShader内容来自http://blog.csdn.net/iispring/article/details/50500106