在上篇說(shuō)道BitmapShader的使用
關(guān)于Shader.TileMode這個(gè)參數(shù)在說(shuō)明一下
Shader.TileMode.CLAMP:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形的四周顏色進(jìn)行填充
效果:填充顏色根據(jù)邊緣填充
Shader.TileMode.REPEAT:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形重復(fù)填充
Shader.TileMode.MIRROR:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形鏡像填充
說(shuō)明一下這里的 Shader.TileMode 可以設(shè)置不同
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
shader有5個(gè)子類(lèi)
LinearGradient使用
float x0, float y0起始xy
float x1, float y1結(jié)束xy
color0起始顏色
color1結(jié)束顏色
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)
兩個(gè)構(gòu)造方法 一個(gè)顏色一個(gè)顏色數(shù)組
LinearGradient linearGradient = new LinearGradient(0,0,200,200, Color.BLUE,Color.RED,Shader.TileMode.CLAMP);
直接上三種效果
RadialGradient使用
centerX,centerY其實(shí)中心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 中心坐標(biāo)
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};
//按順時(shí)針3點(diǎn)鐘為0f,假如我們實(shí)現(xiàn)下半年部分180°為白色,左上90°為灰色,右上90°為藍(lán)色
float[] positions = {0.5f, 0.75f,1f};
//positions 如果null則顏色均勻分布
SweepGradient sweepGradient =
new SweepGradient(canvas.getWidth() / 2f, canvas.getHeight() / 2f, colors, positions );
這里我遇到一個(gè)問(wèn)題一直困惑根據(jù)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,我在其他博客中看到如果設(shè)置不到小于1f,可顯示為帶有缺口的圖形,如果畫(huà)一個(gè)園設(shè)置小雨1.0f的數(shù)值 可顯示扇形
我試了,發(fā)現(xiàn)不行.float[] positions = {0.25f, 0.5f,0.75f};
ComposeShader
ComposeShader,顧名思義,就是混合Shader的意思,它可以將兩個(gè)Shader按照一定的Xfermode組合起來(lái)。
ComposeShader有兩個(gè)構(gòu)造函數(shù),如下所示:
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
如果對(duì)Xfermode不熟悉的話,強(qiáng)烈建議您先讀一下我的另一篇博文《Android中Canvas繪圖之PorterDuffXfermode使用及工作原理詳解》。
此處對(duì)Xfermode做一下簡(jiǎn)單介紹,Xfermode可以用于實(shí)現(xiàn)新繪制的像素與Canvas上對(duì)應(yīng)位置已有的像素按照混合規(guī)則進(jìn)行顏色混合。Xfermode有三個(gè)子類(lèi):AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,其中前兩個(gè)類(lèi)現(xiàn)在被Android廢棄了,現(xiàn)在主要用的是PorterDuffXfermode。PorterDuffXfermode的構(gòu)造函數(shù)需要指定PorterDuff.Mode的類(lèi)型。所以,上面的第二個(gè)構(gòu)造函數(shù)可以看做是第一個(gè)構(gòu)造函數(shù)的特例。我們主要講解第二個(gè),二者大同小異。
我們知道,在使用Xfermode的時(shí)候,存在目標(biāo)像素DST和源像素SRC之說(shuō)。源像素指的是將要向Canvas上繪制的像素,目標(biāo)像素指的是源像素在Canvas上對(duì)應(yīng)位置已經(jīng)存在的像素。
構(gòu)造函數(shù)中的shaderA對(duì)應(yīng)著目標(biāo)像素,shaderB對(duì)應(yīng)著源像素。
有一點(diǎn)需要說(shuō)明,ComposeShader這個(gè)類(lèi)不是必須的,也就是我們不用這個(gè)類(lèi)也能創(chuàng)造對(duì)應(yīng)的效果,它類(lèi)似于一個(gè)助手類(lèi),為我們實(shí)現(xiàn)某種效果提供了方便,下面舉例說(shuō)明。
我們有如下透明圖片:
上面的圖片是透明的,不過(guò)圖片中有個(gè)心形圖案是白色,不透明。 我想讓漸變顏色只填充上圖中的?形區(qū)域,透明部分不填充,顏色從綠色漸變到藍(lán)色,漸變方向從左上角到右下角。我們不用ComposeShader即可實(shí)現(xiàn)上述效果,代碼如下所示:
int bitmapWidth = bitmap.getWidth();int bitmapHeight = bitmap.getHeight();//將繪制代碼放入到canvas.saveLayer()和canvas.restore()之間canvas.saveLayer(0, 0, bitmapWidth, bitmapHeight, null, Canvas.ALL_SAVE_FLAG); //創(chuàng)建BitmapShader,用以繪制?形 BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); //將BitmapShader作為畫(huà)筆paint繪圖所使用的shader paint.setShader(bitmapShader); //用BitmapShader繪制矩形 canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint); //將畫(huà)筆的Xfermode設(shè)置為PorterDuff.Mode.MULTIPLY模式 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); //創(chuàng)建LinearGradient,用以產(chǎn)生從左上角到右下角的顏色漸變效果 LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP); //將創(chuàng)建LinearGradient作為畫(huà)筆paint繪圖所使用的shader paint.setShader(linearGradient); //用LinearGradient繪制矩形 canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint); //最后將畫(huà)筆去除掉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);
//創(chuàng)建BitmapShader,用以繪制?形
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//將BitmapShader作為畫(huà)筆paint繪圖所使用的shader
paint.setShader(bitmapShader);
//用BitmapShader繪制矩形
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
//將畫(huà)筆的Xfermode設(shè)置為PorterDuff.Mode.MULTIPLY模式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
//創(chuàng)建LinearGradient,用以產(chǎn)生從左上角到右下角的顏色漸變效果
LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
//將創(chuàng)建LinearGradient作為畫(huà)筆paint繪圖所使用的shader
paint.setShader(linearGradient);
//用LinearGradient繪制矩形
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
//最后將畫(huà)筆去除掉Xfermode
paint.setXfermode(null);
canvas.restore();
效果如下所示:
如果認(rèn)真讀過(guò)博文《Android中Canvas繪圖之PorterDuffXfermode使用及工作原理詳解》的話,我相信大家應(yīng)該能明白上圖出現(xiàn)的原因。
此處我們還是一起分析一下代碼的執(zhí)行過(guò)程。
我們的圖片中間的?形區(qū)域是純白色,該區(qū)域的像素顏色值A(chǔ)RGB分量是(255,255,255,255)。?形區(qū)域以外的區(qū)域是純透明的,該區(qū)域的像素顏色值A(chǔ)RGB分量是(0,0,0,0)。
為了使用Xfermode,我們將繪圖的代碼放到了canvas.saveLayer()和canvas.restore()之間,對(duì)此有疑問(wèn)的同學(xué)可以參見(jiàn)我上述提到的博文。canvas.saveLayer()會(huì)創(chuàng)建一個(gè)新的繪圖圖層,而且該圖層是全透明的,我們后面的代碼都是繪制到這個(gè)圖層上,而不是直接繪制到Canvas上。
我們用上述Bitmap創(chuàng)建了一個(gè)BitmapShader,并將其綁定到畫(huà)筆Paint中。當(dāng)我們用canvas.drawRect()繪制矩形時(shí),就會(huì)用該BitmapShader填充,此時(shí)的效果應(yīng)該是在新創(chuàng)建的layer上繪制了一個(gè)白色的心形。
然后我們創(chuàng)建了一個(gè)PorterDuffXfermode的實(shí)例,并通過(guò)paint.setXfermode()將其綁定到畫(huà)筆paint上。其中PorterDuffXfermode的mode類(lèi)型為MULTIPLY。MULTIPLY的意思是將源像素的ARGB四個(gè)分量分別與目標(biāo)像素對(duì)應(yīng)的ARGB四個(gè)分量相乘,將相乘的結(jié)果作為混合后的像素。此處進(jìn)行相乘時(shí),ARGB四個(gè)分量都已經(jīng)從[0, 255]的區(qū)間歸一化到[0.0, 1.0]的區(qū)間。
然后我們創(chuàng)建了一個(gè)LinearGradient,用以實(shí)現(xiàn)顏色線性漸變效果。顏色從左上角的綠色漸變到右下角的藍(lán)色。然后我們通過(guò)paint.setShader()方法將其綁定到畫(huà)筆paint的shader上。
后面我們?cè)俅握{(diào)用canvas.drawRect()繪制同樣大小的一個(gè)矩形。在繪制時(shí),我們的畫(huà)筆已經(jīng)同時(shí)綁定了Xfermode和Shader。首先canvas會(huì)用LinearGradient繪制一個(gè)具有漸變色的矩形區(qū)域。然后根據(jù)畫(huà)筆設(shè)置的PorterDuff.Mode.MULTIPLY類(lèi)型,將那些由漸變色填充的矩形區(qū)域中的像素與我們?cè)诘?步中繪制的心形圖片中的像素顏色進(jìn)行相乘混合。漸變色填充的矩形區(qū)域中的像素是源像素,第3步中繪制的心形圖片中的像素是目標(biāo)像素。目標(biāo)像素中?形區(qū)域是純白色的,其像素顏色是(255,255,255,255),歸一化后的顏色是(1,1,1,1),對(duì)應(yīng)位置的源像素中的ARGB顏色分量與其相乘,最終的顏色還是源像素的顏色,即心形區(qū)域被源像素著上了漸變色。目標(biāo)像素中?形區(qū)域以外的顏色是純透明的,顏色是(0,0,0,0),對(duì)應(yīng)位置的源像素中的ARGB顏色分量與其相乘,最終的顏色還是目標(biāo)像素中的(0,0,0,0),即心形區(qū)域以外沒(méi)有被著色,依舊呈現(xiàn)透明色。
最后通過(guò)調(diào)用canvas.restore()方法將新創(chuàng)建的layer繪制到Canvas上去,這樣我們就看到最終的效果了。
下面我們看看如和用ComposeShader實(shí)現(xiàn)上述效果,代碼如下所示:
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//創(chuàng)建BitmapShader,用以繪制?形
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//創(chuàng)建LinearGradient,用以產(chǎn)生從左上角到右下角的顏色漸變效果
LinearGradient linearGradient = new LinearGradient(0, 0, bitmapWidth, bitmapHeight, Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
//bitmapShader對(duì)應(yīng)目標(biāo)像素,linearGradient對(duì)應(yīng)源像素,像素顏色混合采用MULTIPLY模式
ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
//將組合的composeShader作為畫(huà)筆paint繪圖所使用的shader
paint.setShader(composeShader);
//用composeShader繪制矩形區(qū)域
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
用ComposeShader實(shí)現(xiàn)的效果與上圖相同,我就不再貼圖了。我們可以看到,使用ComposeShader之后,實(shí)現(xiàn)相同的效果時(shí),代碼量明顯減少了,而且我們也不需要將繪圖代碼放到canvas.saveLayer()和canvas.restore()之間了。
根據(jù)上面的示例,我們可以得出如下結(jié)論: 假設(shè)我們定義了兩個(gè)Shader的變量,shaderA和shaderB,并分別對(duì)這兩個(gè)Shader進(jìn)行了實(shí)例化。 可以使用ComposeShader將二者組合使用,基本代碼如下所示:
ComposeShader composeShader = new ComposeShader(shaderA, shaderB, porterDuffMode);
paint.setShader(composeShader);
canvas.drawXXX(..., paint);
上述代碼等價(jià)于下面的代碼片段:
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();
此處所說(shuō)的以上兩個(gè)代碼片段等價(jià)的前提是,兩個(gè)代碼片段中的canvas.drawXXX(…, paint)方法中調(diào)用的drawXXX方法相同,并且里面?zhèn)魅氲膮?shù)都相同,例如我們之前兩段心形代碼示例中都調(diào)用drawRect()方法且繪制的矩形的位置及尺寸都相同。
ComposeShader內(nèi)容來(lái)自http://blog.csdn.net/iispring/article/details/50500106