android shader使用

在上篇说道BitmapShader的使用
关于Shader.TileMode这个参数在说明一下
Shader.TileMode.CLAMP:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形的四周颜色进行填充
效果:填充颜色根据边缘填充


Paste_Image.png

Shader.TileMode.REPEAT:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形重复填充

Paste_Image.png

Shader.TileMode.MIRROR:画布大于你画的图形时,图形只显示一个,剩下的画布填充由图形镜像填充

Paste_Image.png

说明一下这里的 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);

直接上三种效果

Paste_Image.png

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);
Paste_Image.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容