你的位置:首页 > 操作系统

[操作系统]Android Xfermode 学习笔记


一、概述

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   @Override24   protected void onDraw(Canvas canvas) {25     int defaultWidth = dip2px(85); //26     int defaultdHeight = dip2px(85); //27 28     if (canvas.getHeight() == defaultWidth && canvas.getHeight() == defaultdHeight) {29       //拿到黄色圆形的bitmap30       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       //拿到蓝色矩形的bitmap37       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       //先画圆形,圆形是dest49       canvas.drawBitmap(bitcircle, 0, 0, paint);50       paint.setXfermode(xfermode);51       //后画矩形,矩形是src52       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); // 4     int defaultdHeight = dip2px(85); // 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       //拿到蓝色矩形的bitmap15       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       //设置setXfermode25       paint.setXfermode(xfermode);26       //在圆形的canvas上画矩形,先画圆形,圆形是dest,后画矩形,矩形是src27       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); // 4     int defaultdHeight = dip2px(85); // 5     Drawable drawable = getDrawable(); 6  7     if (canvas.getHeight() == defaultWidth && canvas.getHeight() == defaultdHeight && drawable instanceof BitmapDrawable) { 8       //setBackgroundColor(Color.TRANSPARENT); 9       //拿到原图的bitmap10       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       //拿到圆形的bitmap18       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       //先画原图,原图是dest30       canvas.drawBitmap(bitimg, 0, 0, paint);31       paint.setXfermode(xfermode);32       //后画圆形,圆形是src33       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]