• Android Xfermode 学习笔记


    一、概述

    Xfermode全名transfer-mode,其作用是实现两张图叠加时的混合效果。

    网上流传的关于Xfermode最出名的图来源于AndroidSDK的samples中,名叫Xfermodes.java,效果如下:

     

    二、体验

    提炼出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] 

  • 相关阅读:
    nyist 541最强DE 战斗力
    nyist 231 Apple Tree
    nyist 543 遥 控 器
    nyist 233 Sort it
    nyist 517 最小公倍数
    hdu 1sting
    nyist A+B Problem IV
    nyist 522 Interval
    nyist 117 求逆序数
    nyist 600 花儿朵朵
  • 原文地址:https://www.cnblogs.com/snser/p/5326438.html
Copyright © 2020-2023  润新知