android shader使用

在上篇說(shuō)道BitmapShader的使用
關(guān)于Shader.TileMode這個(gè)參數(shù)在說(shuō)明一下
Shader.TileMode.CLAMP:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形的四周顏色進(jìn)行填充
效果:填充顏色根據(jù)邊緣填充


Paste_Image.png

Shader.TileMode.REPEAT:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形重復(fù)填充

Paste_Image.png

Shader.TileMode.MIRROR:畫(huà)布大于你畫(huà)的圖形時(shí),圖形只顯示一個(gè),剩下的畫(huà)布填充由圖形鏡像填充

Paste_Image.png

說(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);

直接上三種效果

Paste_Image.png

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

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ō)明。
我們有如下透明圖片:

這里寫(xiě)圖片描述

上面的圖片是透明的,不過(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();

效果如下所示:

這里寫(xiě)圖片描述

如果認(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容