1容客、BitmapShader
可以通過為paint指定一個漸變(BitmapShader郊尝,Paint.setShader(shader))二跋,以圖片填充BitmapShader,就可以得到如下圓形圖片流昏。
1扎即、漸變模式
BitmapShader,位圖漸變况凉,是將圖片作為背景谚鄙,繪制到指定的位圖中,如果圖片比為圖小刁绒,則以以下模式進(jìn)行填充:
- TileMode.CLAMP :不平鋪闷营,即AABB型
- TileMode.REPEAT :平鋪,即ABABA型
- TileMode.MIRROR:鏡像平鋪知市,即ABBA型
效果如下
2初狰、Matrix
可以使用 shader.setLocalMatrix(localM)莫杈,使Matrix 和漸變結(jié)合,實現(xiàn) 位移奢入、旋轉(zhuǎn)筝闹、縮放媳叨、拉斜的漸變效果,如下可以縮放被填充的圖片关顷,移動其中心點(diǎn)到位圖的中心點(diǎn)糊秆。
主要代碼如下
BitmapShader shader=new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//計算縮放比例,縮放被填充的圖片议双,移動其中心點(diǎn)到位圖的中心點(diǎn)
Matrix localM=new Matrix();
int bitmapWidth=bitmap.getWidth();
int bitmapHeight=bitmap.getHeight();
float scaleX=width/(bitmapWidth*1.0f);
float scaleY=height/(bitmapHeight*1.0f);
float scale=Math.max(scaleX,scaleY);
localM.setScale(scale,scale);
if(scaleX<scaleY){
localM.postTranslate((bitmapWidth*scaleY-bitmapWidth*scaleX)/2,0);
}else {
localM.postTranslate(0,(bitmapHeight*scaleX-bitmapHeight*scaleY)/2);
}
//縮放痘番、平移漸變
shader.setLocalMatrix(localM);
//為paint指定一個漸變
paint.setShader(shader);
canvas.drawCircle(centerX,centerY,radius,paint);
當(dāng)然除了圓形,也可以繪制其他任意形狀
path.moveTo(0,height/3);
path.lineTo(width,height/3);
path.lineTo(width/4,height);
path.lineTo(width/2,0);
path.lineTo(3*width/4,height);
path.close();
canvas.drawPath(path,paint);
2平痰、xfermode
可以先在畫布上畫圖片汞舱,在將xfermode設(shè)為DST_IN,在畫圓宗雇,如下:
代碼如下
xfermode=new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
...
int width=getMeasuredWidth();
int height=getMeasuredHeight();
int radius=Math.min(width/2,height/2);
int centerX=width/2;
int centerY=height/2;
desbitmap=Bitmap.createScaledBitmap(bitmap,width,height,true);
circleBitmap=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(circleBitmap);
canvas.drawCircle(centerX,centerY,radius,paint);
int layer=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
paint.setXfermode(xfermode);
canvas.drawBitmap(desbitmap,0,0,null);
canvas.drawBitmap(circleBitmap,0,0,paint);
paint.setXfermode(null);
canvas.restoreToCount(layer);
當(dāng)然,也可以是其他形狀
1赔蒲、 關(guān)于xfermode
PorterDuffXfermode泌神,可以將所繪制的圖形的像素與Canvas中對應(yīng)位置的像素按照一定規(guī)則進(jìn)行混合,形成新的像素值舞虱。當(dāng)使用PorterDuffXfermode時欢际,需要將將其作為參數(shù)傳給Paint.setXfermode(Xfermode xfermode)方法,這樣在用該畫筆paint進(jìn)行繪圖時矾兜,Android就會使用傳入的PorterDuffXfermode损趋,如果不想再使用Xfermode,那么可以執(zhí)行Paint.setXfermode(null)焕刮。
一般我們在調(diào)用canvas.drawXXX()方法時都會傳入一個畫筆Paint對象舶沿,Android在繪圖時會先檢查該畫筆Paint對象有沒有設(shè)置Xfermode,如果沒有設(shè)置Xfermode配并,那么直接將繪制的圖形覆蓋Canvas對應(yīng)位置原有的像素括荡;如果設(shè)置了Xfermode,那么會按照Xfermode具體的規(guī)則來更新Canvas中對應(yīng)位置的像素顏色溉旋。就本例來說畸冲,在執(zhí)行canvas.drawCirlce()方法時,畫筆Paint沒有設(shè)置Xfermode對象观腊,所以繪制的黃色圓形直接覆蓋了Canvas上的像素邑闲。當(dāng)我們調(diào)用canvas.drawRect()繪制矩形時,畫筆Paint已經(jīng)設(shè)置Xfermode的值為PorterDuff.Mode.CLEAR梧油,此時Android首先是在內(nèi)存中繪制了這么一個矩形苫耸,所繪制的圖形中的像素稱作源像素(source,簡稱src)儡陨,所繪制的矩形在Canvas中對應(yīng)位置的矩形內(nèi)的像素稱作目標(biāo)像素(destination褪子,簡稱dst)量淌。源像素的ARGB四個分量會和Canvas上同一位置處的目標(biāo)像素的ARGB四個分量按照Xfermode定義的規(guī)則進(jìn)行計算,形成最終的ARGB值嫌褪,然后用該最終的ARGB值更新目標(biāo)像素的ARGB值呀枢。
本例中的Xfermode是PorterDuff.Mode.CLEAR,該規(guī)則比較簡單粗暴笼痛,直接要求目標(biāo)像素的ARGB四個分量全置為0裙秋,即(0,0缨伊,0摘刑,0),即透明色刻坊,所以我們通過canvas.drawRect()在Canvas上繪制了一個透明的矩形泣侮,由于Activity本身屏幕的背景時白色的,所以此處就顯示了一個白色的矩形紧唱。
PorterDuffXfermode支持以下十幾種像素顏色的混合模式隶校,分別為:CLEAR漏益、SRC、DST深胳、SRC_OVER绰疤、DST_OVER、SRC_IN舞终、DST_IN轻庆、SRC_OUT、DST_OUT敛劝、SRC_ATOP余爆、DST_ATOP、XOR夸盟、DARKEN蛾方、LIGHTEN、MULTIPLY上陕、SCREEN桩砰。
我們知道一個像素的顏色由四個分量組成,即ARGB释簿,第一個分量A表示的是Alpha值亚隅,后面三個分量RGB表示了顏色。我們用S代表源像素庶溶,源像素的顏色值可表示為[Sa, Sc]煮纵,Sa中的a是alpha的縮寫懂鸵,Sa表示源像素的Alpha值,Sc中的c是顏色color的縮寫醉途,Sc表示源像素的RGB矾瑰。我們用D代表目標(biāo)像素,目標(biāo)像素的顏色值可表示為[Da, Dc]隘擎,Da表示目標(biāo)像素的Alpha值殴穴,Dc表示目標(biāo)像素的RGB。
源像素與目標(biāo)像素在不同混合模式下計算顏色的規(guī)則如下所示:
SRC:[Sa, Sc]货葬,顯示上層位圖
DST:[Da, Dc]震桶,只顯示下層位圖
SRC_OVER:[Sa + (1 - Sa)Da, Rc = Sc + (1 - Sa)Dc],上下層都顯示蹲姐,運(yùn)算后上層顯示在上面
DST_OVER:[Sa + (1 - Sa)Da, Rc = Dc + (1 - Da)Sc],上下層都顯示柴墩,運(yùn)算后下層顯示在上面
SRC_IN:[Sa * Da, Sc ** Da],取兩層交集部分江咳,內(nèi)容取決于上層
DST_IN:[Sa * Da, Sa ** Dc],取兩層交集部分歼指,內(nèi)容取決于下層
SRC_OUT:[Sa * (1 - Da), Sc (1 - Da)]踩身,取上層非交集部分
DST_OUT:[Da * (1 - Sa), Dc (1 - Sa)]宰掉,取下層非交集部分
SRC_ATOP:[Da, Sc * Da + (1 - Sa) Dc]
DST_ATOP:[Sa, Sa * Dc + Sc (1 - Da)]
XOR:[Sa + Da - 2 Sa * Da, Sc (1 - Da) + (1 - Sa) Dc]
。赁濒。轨奄。
注:xfermode,計算的是重疊部分的最終像素
1拒炎、des(圖片)挪拟、src(空心圓)和最終位圖大小相等,src是空心圓
2击你、des(黃圓)玉组、src(藍(lán)谎柄,矩形)和最終位圖大小相等
參考:Android中Canvas繪圖之PorterDuffXfermode使用及工作原理詳解
2、關(guān)于 Canvas c=new Canvas(mybitmap):
...
Bitmap mybitmap=BitmapFactory.decodeResource(context.getResources(),R.drawable.test3);
boolean isMutable=mybitmap.isMutable();
Canvas c=new Canvas(mybitmap);
...
如果這樣設(shè)置惯雳,會報錯:
Immutable bitmap passed to Canvas constructor
就是說如果bitmap不可改變的情況下朝巫,canvas是不允許進(jìn)行繪制的, 當(dāng)你用BitmapFactory.decodeResource,返回的bitmap是默認(rèn)狀態(tài)下的mIsMutable=false石景。而使用Bitmap.createBitmap()返回的是 true劈猿。
...
Bitmap b=BitmapFactory.decodeResource(context.getResources(),R.drawable.test3,options);
mybitmap=Bitmap.createScaledBitmap(b,b.getWidth(),b.getHeight(),true);
boolean isMutable=mybitmap.isMutable();
Canvas c=new Canvas(mybitmap);
...
這種情況下mybitmap依然是b,因為大小一樣潮孽,就返回了相同的bitmap揪荣。
/**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
* the source bitmap, the source bitmap is returned and no new bitmap is
* created.
*/
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,
boolean filter) {
...
Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
...
return b;
}
參考: 關(guān)于new Canvas(Bitmap)中Bitmap的isMutable的要求、PorterDuffXferMode不正確的真正原因PorterDuffXferMode深入試驗) 往史、在android中畫圓形圖片的幾種辦法仗颈、 自定義控件三部曲之繪圖篇(十)——Paint之setXfermode(一)