一、setXfermode(Xfermode xfermode)

Xfermode国外有大神称之为过渡模式,这种翻译比较贴切但恐怕不易理解,大家也可以直接称之为图像混合模式,因为所谓的“过渡”其实就是图像混合的一种,这个方法跟我们上面讲到的setColorFilter蛮相似的。查看API文档发现其果然有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,这三个子类实现的功能要比setColorFilter的三个子类复杂得多。

由于AvoidXfermode, PixelXorXfermode都已经被标注为过时了,所以这次主要研究的是仍然在使用的PorterDuffXfermode:

二、PorterDuffXfermode

该类同样有且只有一个含参的构造方法PorterDuffXfermode(PorterDuff.Mode mode),虽说构造方法的签名列表里只有一个PorterDuff.Mode的参数,但是它可以实现很多酷毙的图形效果!!而PorterDuffXfermode就是图形混合模式的意思,其概念最早来自于SIGGRAPH的Tomas Proter和Tom Duff,混合图形的概念极大地推动了图形图像学的发展,延伸到计算机图形图像学像Adobe和AutoDesk公司著名的多款设计软件都可以说一定程度上受到影响,而我们PorterDuffXfermode的名字也来源于这俩人的人名组合PorterDuff,那PorterDuffXfermode能做些什么呢?我们先来看一张API DEMO里的图片:

这张图片从一定程度上形象地说明了图形混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,在API中Android为我们提供了18种(比上图多了两种ADD和OVERLAY)模式: 

  ADD:饱和相加,对图像饱和度进行相加,不常用

  CLEAR:清除图像

  DARKEN:变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合

  DST:只显示目标图像

  DST_ATOP:在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】,相交处的效果受到源图像和目标图像alpha的影响

  DST_IN:只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响

  DST_OUT:只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤

  DST_OVER:将目标图像放在源图像上方

  LIGHTEN:变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关

  MULTIPLY:正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值

  OVERLAY:叠加

  SCREEN:滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖

  SRC:只显示源图像

  SRC_ATOP:在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】,相交处的效果受到源图像和目标图像alpha的影响

  SRC_IN:只在源图像和目标图像相交的地方绘制【源图像】

  SRC_OUT:只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤

  SRC_OVER:将源图像放在目标图像上方

  XOR:在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制

而上面这张图片对应的官方DEMO代码如下:https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/Xfermodes.java

public class sampleActivity extends AppCompatActivity {
// create a bitmap with a circle, used for the "dst" image
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
return bm;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static final int W = 200;
private static final int H = 200;
private static final int ROW_MAX = 4; // number of samples per row
private Bitmap mSrcB;
private Bitmap mDstB;
private Shader mBG; // background checker-board pattern
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
private static final String[] sLabels = {
"Clear", "Src", "Dst", "SrcOver",
"DstOver", "SrcIn", "DstIn", "SrcOut",
"DstOut", "SrcATop", "DstATop", "Xor",
"Darken", "Lighten", "Multiply", "Screen"
};
public SampleView(Context context) {
super(context);
mSrcB = makeSrc(W, H);
mDstB = makeDst(W, H);
// make a ckeckerboard pattern
Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
Bitmap.Config.RGB_565);
mBG = new BitmapShader(bm,
Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
Matrix m = new Matrix();
m.setScale(6, 6);
mBG.setLocalMatrix(m);
}
@Override protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextAlign(Paint.Align.CENTER);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.translate(15, 35);
int x = 0;
int y = 0;
for (int i = 0; i < sModes.length; i++) {
// draw the border
paint.setStyle(Paint.Style.STROKE);
paint.setShader(null);
canvas.drawRect(x - 0.5f, y - 0.5f,
x + W + 0.5f, y + H + 0.5f, paint);
// draw the checker-board pattern
paint.setStyle(Paint.Style.FILL);
paint.setShader(mBG);
canvas.drawRect(x, y, x + W, y + H, paint);
// draw the src/dst example into our offscreen bitmap
int sc = canvas.saveLayer(x, y, x + W, y + H, null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.translate(x, y);
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
// draw the label
canvas.drawText(sLabels[i],
x + W/2, y - labelP.getTextSize()/2, labelP);
x += W + 10;
// wrap around when we've drawn enough for one row
if ((i % ROW_MAX) == ROW_MAX - 1) {
x = 0;
y += H + 30;
}
}
}
}
}

对于上面这些mode的详细介绍在GA_STUDIO的这篇文章和AIGESTUDIO的这篇文章都有非常详尽的介绍

三、使用场景

以下是PorterDuffXfermode的一些使用场景:

1、自定义loading样式:

代码如下:

public class LogoLoadingView extends View {

    private int totalW,totalH;
private Paint paint;
private Bitmap bitmap;
private int currentTop;
private RectF rectF;
private PorterDuffXfermode xfermode; public LogoLoadingView(Context context) {
super(context);
init();
} public LogoLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} private void init(){
paint=new Paint();
paint.setAntiAlias(true);//设置抗锯齿
paint.setStyle(Paint.Style.FILL);//设置填充样式
paint.setDither(true);//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
paint.setFilterBitmap(true);//加快显示速度,本设置项依赖于dither和xfermode的设置
bitmap= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);//从资源文件中解析获取Bitmap
xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
/**
* 设置当前矩形的高度为0
*/
currentTop=bitmap.getHeight();
rectF=new RectF(0,currentTop,bitmap.getWidth(),bitmap.getHeight());
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(bitmap.getWidth(),bitmap.getHeight());
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rectF.top=currentTop;
/**
* 设置View的离屏缓冲。在绘图的时候新建一个“层”,所有的操作都在该层而不会影响该层以外的图像
* 必须设置,否则设置的PorterDuffXfermode会无效,具体原因不明
*/
int sc=canvas.saveLayer(0,0,totalW,totalH,paint,Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(bitmap,0,0,null);
paint.setXfermode(xfermode);
paint.setColor(Color.RED);
canvas.drawRect(rectF,paint);
paint.setXfermode(null);
/**
* 还原画布,与canvas.saveLayer配套使用
*/
canvas.restoreToCount(sc);
if (currentTop>0){
currentTop--;
postInvalidate();
}
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
totalW=w;
totalH=h;
}
}

2、圆形图片

代码如下:

public class CircleImageView extends View {
private int resId;
private Bitmap bitmap;
private Paint paint;
private int bitmapWidth,bitmapHeight;
private int size;
private PorterDuffXfermode xfermode; public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
resId=array.getResourceId(R.styleable.CircleImageView_imageRes,R.mipmap.ic_launcher);
array.recycle();
init();
} private void init(){
paint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setDither(true);//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
paint.setFilterBitmap(true);//加快显示速度,本设置项依赖于dither和xfermode的设置
bitmap= BitmapFactory.decodeResource(getResources(),resId);
bitmapWidth=bitmap.getWidth();
bitmapHeight=bitmap.getHeight();
xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);
size=Math.min(bitmapWidth,bitmapHeight);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(size,size);
} /**
* 生成圆形Bitmap
* @return
*/
private Bitmap makeCircle(){
Bitmap bitmap=Bitmap.createBitmap(size,size, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(bitmap);
Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
int radius=size/2;
canvas.drawCircle(size/2,size/2,radius,paint);
return bitmap;
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int sc=canvas.saveLayer(0,0,size,size,paint,Canvas.ALL_SAVE_FLAG);
Bitmap dst=makeCircle();
canvas.drawBitmap(dst,0,0,paint);
paint.setXfermode(xfermode);
canvas.drawBitmap(bitmap,0,0,paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
}
}

对应属性定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleImageView">
<attr name="imageRes" format="reference"/>
</declare-styleable>
</resources>

关于圆形图片的详细介绍可以看鸿洋大大的这篇博文,介绍的非常详尽:

http://blog.csdn.net/lmj623565791/article/details/42094215

关于PorterDuffXfermode的应用还有非常多,这里简单介绍这几个。

以上内容大部分参考自:

http://blog.csdn.net/tianjian4592/article/details/44783283

http://blog.csdn.net/aigestudio/article/details/41316141

http://www.cnblogs.com/tianzhijiexian/p/4297172.html

Android Paint Xfermode 学习小结的更多相关文章

  1. 【转】Android Paint之 setXfermode PorterDuffXfermode 讲解

    [置顶] Android Paint之 setXfermode PorterDuffXfermode 讲解 分类: android动效篇2015-04-07 17:23 978人阅读 评论(8) 收藏 ...

  2. Android Paint的使用以及方法介绍(附源码下载)

    要绘图,首先得调整画笔,待画笔调整好之后,再将图像绘制到画布上,这样才可以显示在手机屏幕上.Android 中的画笔是 Paint类,Paint 中包含了很多方法对其属性进行设置,主要方法如下: se ...

  3. 点滴的积累---J2SE学习小结

    点滴的积累---J2SE学习小结 什么是J2SE J2SE就是Java2的标准版,主要用于桌面应用软件的编程:包括那些构成Java语言核心的类.比方:数据库连接.接口定义.输入/输出.网络编程. 学习 ...

  4. Android之SurfaceView学习(一)转转

    Android之SurfaceView学习(一) 首先我们先来看下官方API对SurfaceView的介绍 SurfaceView的API介绍 Provides a dedicated drawing ...

  5. Android ---paint类

    引自:http://www.cnblogs.com/-OYK/archive/2011/10/25/2223624.html Android Paint和Color类   要绘图,首先得调整画笔,待画 ...

  6. Cocos2d-x学习小结 配置篇

    Cocos2d-x学习小结 配置篇 学习工具:Cocos2d-x用户手册,<Cocos2d-x游戏开发之旅> 首先官网下载cocos2d-x源码,安装vs2019.如果没有安装python ...

  7. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  8. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  9. react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)

    react学习小结   本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...

随机推荐

  1. Hibernate介绍和入门案例

    一身转战三千里,一剑曾当百万师 如果你在之前没有学过SSH三大框架,那么你之前肯定是通过JDBC来对数据库进行操作.现在,你完全可以把跟数据库交互的操作直接交给Hibernate. Hibernate ...

  2. windows系统下快捷命令

    mstsc 远程计算机 regedit 注册表

  3. Smallest multiple

    problem 5:Smallest multiple 题意:求最小的正数,使得其可以被1-20整除 代码如下: #ifndef PRO5_H_INCLUDED #define PRO5_H_INCL ...

  4. angularjs过滤器(一)------禁止转载------

    如果用代码实现:将new Date()转换为 M/D/YY 00:00 格式. 启用过滤器有两种方式:①在HTML中用"|"启用过滤器.格式为{{$scope.property | ...

  5. IOS GCD使用实例大全

    GCD是大家在IOS开发过程中经常使用的一种多线程管理机制.原理这里就不多说了,大家关心的大部分都是它的使用,下面主要介绍GCD的主要方法及其实例. 1.认识主队列,感受串行队列的运行,运行结果打印的 ...

  6. svn服务器搭建-SuSE Linux Enterprise Server 11 SP3

    svn存储版本数据也有2种方式: 1.bdb: 2.fsfs. 因为BDB方式在服务器中断时,有可能锁住数据(搞ldap时就深受其害,没法根治),所以还是FSFS方式更安全一点,我也选择这种方式.   ...

  7. 【css2、css3】css改变select选择框的样式

    效果: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  8. sparksql中行转列

    进入sparksql beeline -u "jdbc:hive2://172.16.12.46:10015" -n spark -p spark -d org.apache.hi ...

  9. sublime 2中Package control安装和使用

    安装: 安装时,如果想查看安装进度,可打开console(View->Show Console) 安装Package control有两中方法: 方法1:通过代码安装 import urllib ...

  10. LED :制作一个追逐序列(霹雳游侠)

    ; ,,}; ; void setup() { ; led<NbrLeds; led++){ pinMode(ledPins[led], OUTPUT); } } void loop() { ; ...