在上篇說道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