上一篇博客实现了android自定义圆形图像这篇博客给大家实现基于xfermode的遮罩层画板功能。首先我会先实现一个简单的画板功能。
实现简单的画板功能
这里我们的画板是全屏实现的,所以先写一个工具类ScreenUtil来获得屏幕的宽和高的值。
ScreenUtil.java
public static int[] getScreenSize(Context context) {
int screenSize[] = new int[2];
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display metrics = windowManager.getDefaultDisplay();
screenSize[0] = metrics.getWidth();
screenSize[1] = metrics.getHeight();
return screenSize;
}
这里我将宽和高封装到了一个整型数组里然后然后返回该数组。
新建DrawView类继承自View
这里我们新建一个DrawView extends View ,然后在构造方法里进行初始化,DrawView的属性如下:
private Paint mPaint = null; //用于绘制路径的画笔
private Path mPath = null; //用户触摸的路径
private Bitmap desBitmap = null;
private Canvas mCanvas = null;
private float preX; //上一次的触摸点x坐标
private float preY; //上一次触摸点y坐标
private static final int MIN_SLOP = 5; //最小的滑动距离
在构造方法中进行初始化:
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);//设置画笔抗锯齿和抗抖动
mPaint.setStyle(Paint.Style.STROKE); //设置画笔为实心
mPaint.setStrokeCap(Paint.Cap.ROUND); //设置画笔笔触为圆形
mPaint.setStrokeJoin(Paint.Join.ROUND); //设置画笔接触点为圆形
mPaint.setStrokeWidth(30); //画笔的宽度
mPaint.setColor(Color.GREEN); //画笔的颜色
//获取当前屏幕的宽和高
int screenWidth = ScreenUtil.getScreenSize(getContext())[0];
int screenHeight = ScreenUtil.getScreenSize(getContext())[1];
//创建一个BITMAP
desBitmap = Bitmap.createBitmap(screenWidth,screenHeight,Config.ARGB_8888);
//创建一个和当前bitmap大小相同的画布
mCanvas = new Canvas(desBitmap);
//绘制当前画布的填充色
mCanvas.drawColor(Color.parseColor("#cccccc"));
重写onDraw方法
//将desBitmap绘制到当前view的canvas中
canvas.drawBitmap(desBitmap, 0, 0, null);
//绘制path,即用户滑动的路径
mCanvas.drawPath(mPath, mPaint);
这里还需要重写onTouchEvent方法,用来根据用户手指的滑动位置,实现mPath具体的绘制工作。
重写onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "the onTouchEvent runs....");
//获取当前手指相对当前view接触的x,y坐标
/**
* 获取当前手指相对当前手机屏幕坐标,接触的x,y坐标
* event.getRawX()
* event.getRawY()
*/
float currentX = event.getX();
float currentY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "the action_down runs....");
//当用户按下时候,将mPath重置,并且将起始点移动到当前坐标
mPath.reset();
mPath.moveTo(currentX, currentY);
//记录上次触摸的坐标,注意ACTION_DOWN方法只会执行一次
preX = currentX;
preY = currentY;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "the action_move runs....");
//ACTION_MOVE方法会多次执行,计算用户水平和垂直方向滑动的距离
int dx = (int) Math.abs(currentX- preX);
int dy = (int) Math.abs(currentY - preY);
//如果滑动的距离大于规定的值
if (dx > MIN_SLOP || dy > MIN_SLOP) {
mPath.quadTo(preX, preY, currentX, currentY);
//重新为上一次的触摸点赋值
preX = currentX;
preY = currentY;
}
break;
}
invalidate();//重新绘制当前view
return true;
}
这里在onTouchEvent方法最后返回true表示当前view会消耗掉该事件,如果事件返回false,会将事件再次返回给其父控件来执行。Path可以实现两点之间的平滑过渡。
此时效果如下:
使用xfermode实现遮罩层画板
好了,到现在为止我们已经知道如何实现一个基本的画板功能,下面通过结合PorterDuffXfermode来实现遮罩层的画板功能。核心代码如下:
private void init() {
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(40);
mPaint.setColor(Color.argb(20,255,0,0));
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//获取屏幕的宽高
int screenWidth = ScreenUtil.getScreenSize(getContext())[0];
int screenHeight = ScreenUtil.getScreenSize(getContext())[1];
//创建一个遮罩层,并且使用单独的canvas绘制
mDesBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);
mDrawCanvas = new Canvas(mDesBitmap);
mDrawCanvas.drawColor(Color.parseColor("#cccccc"));
//获取需要被遮罩的图片并且将其缩放值屏幕大小
mSrcBimap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_back);
mSrcBimap = Bitmap.createScaledBitmap(mSrcBimap, screenWidth, screenHeight, false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
先绘制原图像,然后绘制和原图像相同大小的遮罩层
canvas.drawBitmap(mSrcBimap, 0, 0, null);
canvas.drawBitmap(mDesBitmap, 0, 0, null);
//根据mPaint设置的setXfermode来绘制用户滑动的path
mDrawCanvas.drawPath(mPath, mPaint);
}
主要增加了如下几步:
- 首先创建一个和当前屏幕相同大小的mDrawCanvas对象,并且使用颜色将其填充。
/创建一个遮罩层,并且使用单独的canvas绘制
mDesBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);
mDrawCanvas = new Canvas(mDesBitmap); mDrawCanvas.drawColor(Color.parseColor("#cccccc"));
- 为画笔设置Xfermode,也就是以那种方式来显示重叠的部分。
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
- 获取需要被隐藏的图片。
//获取需要被遮罩的图片并且将其缩放值屏幕大小
mSrcBimap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_back);
mSrcBimap = Bitmap.createScaledBitmap(mSrcBimap, screenWidth, screenHeight, false);
- 先绘制原图像,然后绘制和原图像相同大小的遮罩层
canvas.drawBitmap(mSrcBimap, 0, 0, null);
canvas.drawBitmap(mDesBitmap, 0, 0, null);
- 重写onTouchEvent,并且根据mPaint设置的setXfermode来绘制用户滑动的path
mDrawCanvas.drawPath(mPath, mPaint);
onTouchEvent方法是一样的处理方式,这里只是多了一个XferMode的设置。
效果如下: