学习资料:
- Android群英传
1.PorterDuffXfermode
PorterDuffXfermode
有点类似数学中的交集,并集,用来两个图像间的混合显示模式,设置的是两个图层交集区域的显示方式,dst
是下层,先画的图形;src
是上层,后画的图形
构造方法:
PorterDuffXfermode(PorterDuff.Mode mode)
构造方法中只需一个参数,PorterDuff.Mode
,Mode
是PorterDuff
这个类中的一个枚举类,现在Mode
中有18个枚举值,图中只有16个,图少了ADD
和OVERLAY
2.PorterDuff.Mode
上面的图是有错误的,下面的图,和我自己测试的结果是一致的。错误原因可以去Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解看看
最常用的就是DST_IN
和SRC_IN
,是可以用来实现自定义CircleImageView
的一种方式
看图帮助:后来者居上
- 黄色的圆是
DST
下层,先进行绘制; - 蓝色的矩形是
SRC
上层,后进行绘制
模式 | 效果 |
---|---|
PorterDuff.Mode.CLEAR |
上层绘制不会提交到画布,并把与下层交集部分也清除 |
PorterDuff.Mode.SRC |
显示上层绘制,此时下层绘制也会显示 |
PorterDuff.Mode.DST |
显示下层绘制,而上层不会绘制 |
PorterDuff.Mode.SRC_OVER |
正常显示,上层叠盖在下层之上 |
PorterDuff.Mode.DST_OVER |
上下层都显示,下层在上 |
PorterDuff.Mode.SRC_IN |
将交集显示在下层绘制的区域 |
PorterDuff.Mode.DST_IN |
显示下层绘制 |
PorterDuff.Mode.SRC_OUT |
取非交集区域 |
PorterDuff.Mode.DST_OUT |
取下层非交集区域 |
PorterDuff.Mode.SRC_ATOP |
取上层的交集区域和下层的非交集区域 |
PorterDuff.Mode.DST_ATOP |
下层在上层之上 |
PorterDuff.Mode.XOR |
去除两层的交集区域 |
PorterDuff.Mode.DARKEN |
取两层全部区域,交集区域变暗 |
PorterDuff.Mode.LIGHTEN |
取两层全部区域,交集区域变亮 |
PorterDuff.Mode.MULTIPLY |
取下层全部区域,交集区域色彩叠加,正片叠底 |
PorterDuff.Mode.SCREEN |
取两层全部区域,交集区域变透明 |
PorterDuff.Mode.ADD |
饱和度叠加 |
PorterDuff.Mode.OVERLAY |
对黑白无效,显示两层颜色中和后的中间色 |
PorterDuff.Mode
不单单是进行了图形的操作,有的模式还对色彩有影响
这些模式最好都自己测试一下
具体的过程可以学习爱哥的自定义控件其实很简单1/6
有的模式,不支持硬件加速,最好关闭硬件加速
绘制过程后来者居上,dst
为下层,先进行绘制;src
为上层,后进行绘制。
生活中的例子: 鸡蛋灌饼 : )
简单测试:
public class PorterDuffView extends View {
private Paint paint ;
private RectF rectF;
private PorterDuffXfermode porterDuffXfermode;
public PorterDuffView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
private void initPaint() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关闭硬件加速
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
rectF = new RectF(150,150,500,500);
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制背景色
canvas.drawColor(Color.YELLOW);
//创建一个新的画布Layer
int layerId = canvas.saveLayer(0, 0, getWidth(),getHeight() , null, Canvas.ALL_SAVE_FLAG);
//绘制dst层
float x = getWidth() / 4;
float y = getHeight() / 4;
float radius = Math.min(getWidth(), getHeight()) / 4;
paint.setColor(Color.CYAN);
canvas.drawCircle(x, y, radius, paint);
//设置混合模式
paint.setColor(Color.RED);
paint.setXfermode(porterDuffXfermode);
//绘制src层
canvas.drawRect(rectF,paint);
paint.setXfermode(null);
canvas.restoreToCount(layerId);//将自己创建的画布Layer绘制到画布默认的Layer
}
/**
* 测量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, 300);
} else if (wSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, hSpecSize);
} else if (hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(wSpecSize, 300);
}
}
}
之所以更改图层是为了让绘制不受背景色影响。将绘制的圆形和矩形在一个新的图层绘制,绘制完成后,再把新的图层加入到Canvas
的默认图层。
3.CircleImageView
如果只是单单为了显示一个圆形的图片,可以有好多种办法。
可以借助PorterDuffXfermode
或者v4
包下的RoundedBitmapDrawable
进行把Biamp
进行形状上的改变得到圆形
若不是经常大量的频繁的显示出一个圆形图片,也可以利用CardView
,Android 一个另类的显示圆形图片方式
本篇为了学习PorterDuffXfermode
就使用前面提到的PorterDuff.Mode.SRC_IN
简单实践:
public class CircleImageView extends ImageView {
private Paint mPaint;
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
BitmapDrawable drawable = (BitmapDrawable) getDrawable();
if (drawable != null) {
Bitmap bitmap = drawable.getBitmap();
drawTargetBitmap(canvas, bitmap);
}
}
private void drawTargetBitmap(Canvas canvas, Bitmap bitmap) {
final int sc = canvas.saveLayer(0, 0, getWidth(),getHeight(), null, Canvas.ALL_SAVE_FLAG);
//先绘制dst层
final float x = getWidth() / 2;
final float y = getHeight() / 2;
final float radius = Math.min(getWidth(), getHeight()) / 2;
canvas.drawCircle(x, y, radius, mPaint);
//设置混合模式
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//绘制src层
final float f_x = getWidth() / 2 -bitmap.getWidth() / 2;
final float f_y = getHeight() / 2 -bitmap.getHeight() / 2;
canvas.drawBitmap(bitmap, f_x,f_y, mPaint);
// 还原混合模式
mPaint.setXfermode(null);
// 还原画布
canvas.restoreToCount(sc);
}
}
一个超级简单的自定义CircleImageView
4.最后
简单介绍完毕,主要就是18种模式的理解,最好每种模式的效果都能够明白,掌握最常用的DST_IN
和SRC_IN
进一步学习,可以看鸿洋大神的Android 自定义控件实现刮刮卡效果 真的就只是刮刮卡么
5. 补充
9月9号 晚上
关于PorterDuffXfermode
的坑总结的很好,PorterDuffXfermode不正确的真正原因
本人还很菜,有错误,请指出
共勉 : )