如果你对PorterDuff与Xfermode不够了解可以参看我之前的博客Android 绘图进阶(一),上篇博客介绍了Xfermode的SRC_IN的画笔风格的使用实例,这篇博客介绍一下它的一种涂层效果(XOR)。
方案一:圆形画板擦
一、思路
根据graphic图我们可以使用看出Xor效果实现的就是相交部分会露出底部图片。因此我们需要设置一个底部的背景(BitmapBackground),我是赵丽颖粉丝,就选择了她的图片作为背景,之后还需要绘制DST与SRC涂层,由于Xfermode的使用必须基于一张Bitmap,因此我们需要给要绘制的DST与SRC涂层创建一个Bitmap,并为它创建一个新的画布,将该Bitmap绘制到新建的画布上面,并将DST与SRC绘图绘制在上面。最后添加上手势监听就可以了。
二、代码示例
1、跟之前一样先创建用于绘制DST与SRC的Bitmap与它Canvas
//width、height是在onMeasure方法中获得的因此要在onMeasure创建bitmap
//并设置Bitmap的大小充满屏幕
mBitmap=Bitmap.createBitmap(width, height, Config.ARGB_8888);
//创建Bitmap图的画布
BitmapCanvas=new Canvas(mBitmap);
2、DST与SRC
//系统默认先画的为DST
BitmapCanvas.drawRect(0, 0, width, height, mpaintcircle);
//后绘制的为SRC
BitmapCanvas.drawPath(mpath, mpaintrect);
//将bitmap图片绘制到画布上面
canvas.drawBitmap(mBitmap, 0, 0, null);
3、设置xfermode
//给画笔设置Xfermode
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
mpaintrect.setXfermode(mode);
4、添加上手势监听
//手势监听
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:{
x=event.getX();
y=event.getY();
//为了不是单纯的绘制一个圆,使用Path路径
mpath.addCircle(x, y, 50, Direction.CW);
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
x=event.getX();
y=event.getY();
mpath.addCircle(x, y, 50, Direction.CW);
invalidate();
return true;
default:
break;
}
return super.onTouchEvent(event);
}
5、完整代码
布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.example.myview.MyBitmapView2
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
创建继承View的class
public class MyBitmapView2 extends View{
private int width;
private int height;
private Paint mpaintcircle;
private Paint mpaintrect;
//用于绘制DST与SRC的Bitmap
private Bitmap mBitmap;
//背景
private Bitmap mBitmapBackground;
//DST与SRC的画布
private Canvas BitmapCanvas;
private Bitmap back;
//用于绘制圆路径
private Path mpath;
public MyBitmapView2(Context context) {
super(context);
}
public MyBitmapView2(Context context, AttributeSet attrs) {
super(context, attrs);
//设置dst画笔
mpaintcircle=new Paint();
mpaintcircle.setColor(Color.YELLOW);
//设置src画笔的颜色
mpaintrect=new Paint();
mpaintrect.setColor(Color.GREEN);
//给画笔设置Xfermode
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
mpaintrect.setXfermode(mode);
mpath=new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画布的背景图片,矩形区域由back确定
canvas.drawBitmap(mBitmapBackground, new Rect(0,0,back.getWidth(),back.getHeight()), new Rect(0,0,width,height), null);
//系统默认先画的为DST
BitmapCanvas.drawRect(0, 0, width, height, mpaintcircle);
//后绘制的为SRC
BitmapCanvas.drawPath(mpath, mpaintrect);
//将bitmap图片绘制到画布上面
canvas.drawBitmap(mBitmap, 0, 0, null);
}
private float x;
private float y;
//手势监听
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:{
x=event.getX();
y=event.getY();
//为了不是单纯的绘制一个圆,使用Path路径
mpath.addCircle(x, y, 50, Direction.CW);
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
x=event.getX();
y=event.getY();
mpath.addCircle(x, y, 50, Direction.CW);
invalidate();
return true;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
//告知父布局该View的大小
setMeasuredDimension(width, height);
mBitmap=Bitmap.createBitmap(width, height, Config.ARGB_8888);
//创建Bitmap图的画布
BitmapCanvas=new Canvas(mBitmap);
//创建Bitmap背景图片
mBitmapBackground=BitmapFactory.decodeResource(getResources(), R.drawable.zly);
//创建它的原因是为了绘制mBitmapBackground时,确定矩形区域(全屏)
back=Bitmap.createBitmap(width, height, Config.ARGB_8888);
}
}
这样我们的涂层效果就制作好了!
方案二:线型画板擦
可以看到我们上面的图片的画板擦的形状是圆形的,这里想要把它进一步修改为线型的板擦,我们只需要修改我们手势监听部分的mpath和画笔的线型圆角与中间连线的圆形连接以及非填充Style就可以了。
下面将修改的中点代码提取出来
画笔
//注:使用setStrokeCap与setStrokeJoin必须先设置style
mpaintrect.setStyle(Style.STROKE);
mpaintrect.setStrokeCap(Cap.ROUND);
mpaintrect.setStrokeJoin(Join.ROUND);
手势监听
private float old_x;
private float old_y;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:{
x=event.getX();
y=event.getY();
//mpath.addCircle(x, y, 50, Direction.CW);
mpath.moveTo(x, y);
invalidate();
old_x=x;
old_y=y;
return true;
}
case MotionEvent.ACTION_MOVE:
x=event.getX();
y=event.getY();
//每次移动都从落下位置划线到移动位置
mpath.moveTo(old_x,old_y);
//这里可以使用lineto也可以使用正余弦曲线
mpath.lineTo(x,y);
//mpath.rQuadTo((x+old_x)/2,(y+old_y), x, y);
//刷新UI
invalidate();
old_x=x;
old_y=y;
return true;
default:
break;
}
return super.onTouchEvent(event);
}
下面是完整代码
public class MyBitmapView2 extends View{
private int width;
private int height;
private Paint mpaintcircle;
private Paint mpaintrect;
private Bitmap mBitmap;
private Bitmap mBitmapBackground;
private Canvas BitmapCanvas;
private Bitmap back;
private Path mpath;
public MyBitmapView2(Context context) {
super(context);
}
public MyBitmapView2(Context context, AttributeSet attrs) {
super(context, attrs);
//设置圆形画笔
mpaintcircle=new Paint();
mpaintcircle.setColor(Color.YELLOW);
//设置矩形画笔的颜色
mpaintrect=new Paint();
mpaintrect.setColor(Color.GREEN);
//给画笔设置mode
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
mpaintrect.setXfermode(mode);
//注:使用setStrokeCap与setStrokeJoin必须先设置style
mpaintrect.setStyle(Style.STROKE);
mpaintrect.setStrokeCap(Cap.ROUND);
mpaintrect.setStrokeJoin(Join.ROUND);
mpaintrect.setStrokeWidth(30);
mpath=new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画布的背景色
canvas.drawBitmap(mBitmapBackground,0,0, null);
//在bitmap图的画布上绘制圆形与矩形
// BitmapCanvas.drawCircle(width/2, height/2, width/2, mpaintcircle);//dst
BitmapCanvas.drawRect(0, 0, width, height, mpaintcircle);//src
BitmapCanvas.drawPath(mpath, mpaintrect);
//将bitmap图片绘制到画布上面
canvas.drawBitmap(mBitmap, 0, 0, null);
}
private float x;
private float y;
private float old_x;
private float old_y;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:{
x=event.getX();
y=event.getY();
//mpath.addCircle(x, y, 50, Direction.CW);
mpath.moveTo(x, y);
invalidate();
old_x=x;
old_y=y;
return true;
}
case MotionEvent.ACTION_MOVE:
x=event.getX();
y=event.getY();
mpath.moveTo(old_x,old_y);
mpath.lineTo(x,y);
//mpath.rQuadTo((x+old_x)/2,(y+old_y), x, y);
invalidate();
old_x=x;
old_y=y;
return true;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
mBitmap=Bitmap.createBitmap(width, height, Config.ARGB_8888);
//创建Bitmap图的画布
BitmapCanvas=new Canvas(mBitmap);
mBitmapBackground=BitmapFactory.decodeResource(getResources(), R.drawable.zly);
back=Bitmap.createBitmap(width, height, Config.ARGB_8888);
// Canvas canva=new Canvas(mBitmapBackground);
// canva.drawBitmap(mBitmapBackground, new Rect(0,0,mBitmapBackground.getWidth(),mBitmapBackground.getHeight()), new Rect(0,0,width,height), null);
}
}
package com.example.myview;
public class MainActivity_bitmap2 extends Activity implements OnClickListener{
private Button mbtn_bitmap2;
private MyBitmapView2 bitmap2;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.bitmap_view2);
mbtn_bitmap2=(Button) findViewById(R.id.btn_bitmapview2);
bitmap2=(MyBitmapView2) findViewById(R.id.bitmap2);
mbtn_bitmap2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
File file=new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg");
bitmap2.invalidate();
bitmap2.setDrawingCacheEnabled(true);
Bitmap bitmapnew=bitmap2.getDrawingCache();
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Log.d("路径", file.getAbsolutePath());
bitmapnew.compress(CompressFormat.JPEG,100, new FileOutputStream(file));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}