一、概述 Xfermode全名transfer-mode,其作用是實現兩張圖疊加時的混合效果。 網上流傳的關於Xfermode最出名的圖來源於AndroidSDK的samples中,名叫Xfermodes.java,效果如下: [轉載請保留本文地址:http://www.cnblogs.com/go ...
一、概述
Xfermode全名transfer-mode,其作用是實現兩張圖疊加時的混合效果。
網上流傳的關於Xfermode最出名的圖來源於AndroidSDK的samples中,名叫Xfermodes.java,效果如下:
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5326438.html]
二、體驗
提煉出Xfermodes.java中的核心代碼,自己寫了個簡單粗暴的demo試試水:
1 public class ImageViewXfermode extends ImageView { 2 public ImageViewXfermode(Context context) { 3 super(context); 4 init(); 5 } 6 7 public ImageViewXfermode(Context context, AttributeSet attrs) { 8 super(context, attrs); 9 init(); 10 } 11 12 public ImageViewXfermode(Context context, AttributeSet attrs, int defStyle) { 13 super(context, attrs, defStyle); 14 init(); 15 } 16 17 private void init() { 18 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 19 setLayerType(View.LAYER_TYPE_SOFTWARE, null); 20 } 21 } 22 23 @Override 24 protected void onDraw(Canvas canvas) { 25 int defaultWidth = dip2px(85); //xml里view的寬度是85dp 26 int defaultdHeight = dip2px(85); //xml里view的高度是85dp 27 28 if (canvas.getHeight() == defaultWidth && canvas.getHeight() == defaultdHeight) { 29 //拿到黃色圓形的bitmap 30 Bitmap bitcircle = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 31 Canvas canvascicle = new Canvas(bitcircle); 32 Paint paintcicle = new Paint(Paint.ANTI_ALIAS_FLAG); 33 paintcicle.setColor(0xFFFFCC44); 34 canvascicle.drawCircle(dip2px(30), dip2px(30), dip2px(25), paintcicle); 35 36 //拿到藍色矩形的bitmap 37 Bitmap bitrect = Bitmap.createBitmap(defaultWidth, defaultdHeight, Config.ARGB_8888); 38 Canvas canvasrect = new Canvas(bitrect); 39 Paint paintrect = new Paint(Paint.ANTI_ALIAS_FLAG); 40 paintrect.setColor(0xFF66AAFF); 41 canvasrect.drawRect(dip2px(30), dip2px(30), dip2px(80), dip2px(80), paintrect); 42 43 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 44 Xfermode xfermode = new PorterDuffXfermode(Mode.LIGHTEN); 45 46 //採用saveLayer,讓後續canvas的繪製在自動創建的bitmap上 47 int cnt = canvas.saveLayer(0, 0, defaultWidth, defaultdHeight, null, Canvas.ALL_SAVE_FLAG); 48 //先畫圓形,圓形是dest 49 canvas.drawBitmap(bitcircle, 0, 0, paint); 50 paint.setXfermode(xfermode); 51 //後畫矩形,矩形是src 52 canvas.drawBitmap(bitrect, 0, 0, paint); 53 paint.setXfermode(null); 54 canvas.restoreToCount(cnt); 55 } else { 56 super.onDraw(canvas); 57 } 58 } 59 60 private int dip2px(float dip) { 61 float scale = getResources().getDisplayMetrics().density; 62 return (int)(dip * scale + 0.5f); 63 } 64 }
效果如下:
介紹一下幾個關鍵點:
1、關於src和dest
先繪製到canvas上的是dest,後繪製的是src
2、關於硬體加速
在sdkversion>=11時,需要關閉硬體加速(第19行),否則 Mode.CLEAR 、 Mode.DARKEN 、 Mode.LIGHTEN 三種模式下繪製效果不正常
3、saveLayer的作用
Canvas.saveLayer在Canvas.save的基礎上,額外自動分配了一個bitmap,使得saveLayer之後的所有繪製都在這個新分配的bitmap上完成。
如果把saveLayer去掉呢?效果就是藍色矩形跟黃色圓形和灰色背景都進行了 Mode.LIGHTEN 操作:
4、如果不saveLayer
有一種變通的方法,在dest對應bitmap的canvas上繪製src對應的bitmap,這樣的目的與saveLayer是一致的,不在當前canvas上直接繪圖:
1 @Override 2 protected void onDraw(Canvas canvas) { 3 int defaultWidth = dip2px(85); //xml里view的寬度是85dp 4 int defaultdHeight = dip2px(85); //xml里view的高度是85dp 5 6 if (canvas.getHeight() == defaultWidth && canvas.getHeight() == defaultdHeight) { 7 //拿到黃色圓形的bitmap 8 Bitmap bitcircle = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 9 Canvas canvascicle = new Canvas(bitcircle); 10 Paint paintcicle = new Paint(Paint.ANTI_ALIAS_FLAG); 11 paintcicle.setColor(0xFFFFCC44); 12 canvascicle.drawCircle(dip2px(30), dip2px(30), dip2px(25), paintcicle); 13 14 //拿到藍色矩形的bitmap 15 Bitmap bitrect = Bitmap.createBitmap(defaultWidth, defaultdHeight, Config.ARGB_8888); 16 Canvas canvasrect = new Canvas(bitrect); 17 Paint paintrect = new Paint(Paint.ANTI_ALIAS_FLAG); 18 paintrect.setColor(0xFF66AAFF); 19 canvasrect.drawRect(dip2px(30), dip2px(30), dip2px(80), dip2px(80), paintrect); 20 21 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 22 Xfermode xfermode = new PorterDuffXfermode(Mode.LIGHTEN); 23 24 //設置setXfermode 25 paint.setXfermode(xfermode); 26 //在圓形的canvas上畫矩形,先畫圓形,圓形是dest,後畫矩形,矩形是src 27 canvascicle.drawBitmap(bitrect, 0, 0, paint); 28 paint.setXfermode(null); 29 //將圓形canvas上畫出來的結果繪製到canvas上 30 canvas.drawBitmap(bitcircle, 0.0f, 0.0f, paint); 31 } else { 32 super.onDraw(canvas); 33 } 34 }
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5326438.html]
三、更多玩法——圓形ImageView
利用Xfermode的特性也可以做出圓形的ImageView來。先畫原圖,再畫圓形,採用 Mode.DST_IN 即可:
1 @Override 2 protected void onDraw(Canvas canvas) { 3 int defaultWidth = dip2px(85); //xml里view的寬度是85dp 4 int defaultdHeight = dip2px(85); //xml里view的高度是85dp 5 Drawable drawable = getDrawable(); 6 7 if (canvas.getHeight() == defaultWidth && canvas.getHeight() == defaultdHeight && drawable instanceof BitmapDrawable) { 8 //setBackgroundColor(Color.TRANSPARENT); 9 //拿到原圖的bitmap 10 Bitmap bitimg = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 11 Canvas canvasimg = new Canvas(bitimg); 12 Matrix matrix = new Matrix(); 13 matrix.setScale(defaultWidth * 1.0f / drawable.getIntrinsicWidth(), defaultdHeight * 1.0f / drawable.getIntrinsicHeight()); 14 Paint paintimg = new Paint(Paint.ANTI_ALIAS_FLAG); 15 canvasimg.drawBitmap(((BitmapDrawable)drawable).getBitmap(), matrix, paintimg); 16 17 //拿到圓形的bitmap 18 Bitmap bitcircle = Bitmap.createBitmap(defaultWidth, defaultdHeight, Config.ARGB_8888); 19 Canvas canvascircle = new Canvas(bitcircle); 20 Paint paintcircle = new Paint(Paint.ANTI_ALIAS_FLAG); 21 paintcircle.setColor(0xFF66AAFF); 22 canvascircle.drawCircle(dip2px(85 / 2.0f), dip2px(85 / 2.0f), dip2px(85 / 2.0f), paintcircle); 23 24 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 25 Xfermode xfermode = new PorterDuffXfermode(Mode.DST_IN); 26 27 //採用saveLayer,讓後續canvas的繪製在自動創建的bitmap上 28 int cnt = canvas.saveLayer(0, 0, defaultWidth, defaultdHeight, null, Canvas.ALL_SAVE_FLAG); 29 //先畫原圖,原圖是dest 30 canvas.drawBitmap(bitimg, 0, 0, paint); 31 paint.setXfermode(xfermode); 32 //後畫圓形,圓形是src 33 canvas.drawBitmap(bitcircle, 0, 0, paint); 34 paint.setXfermode(null); 35 canvas.restoreToCount(cnt); 36 } else { 37 super.onDraw(canvas); 38 } 39 }
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5326438.html]