背景:需要实线一个类似刮刮乐的擦一擦效果,要求是在图片上覆盖半透明蒙层,蒙层支持手势擦除(类似橡皮擦)。
思路:使用自定义View在onDraw时进行绘制,绘制模式选择混合模式(叠加变透明)。
示例:
代码:
package com.xiazy.test;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* @version V1.0
* @Author Xiazy
* @Date 2022/9/10 23:59:59
* @Description: 自定义擦一擦类型动画效果-类似刮刮乐
*/
public class WipeView extends View {
// 擦一擦画笔
private Paint paint;
// 源图像-背景色
private Bitmap decodeResourceSRC;
// 目标图像-手指擦除的位置
private Bitmap createBitmapDST;
// 手指路径,使用贝塞尔路线
private Path path;
// 按下的坐标X
private float perX;
// 按下的坐标Y
private float perY;
public WipeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 设置可擦除范围的擦一擦参数
*
* @param width 宽度
* @param height 高度
* @param callBack 擦一擦事件回调
*/
public void setWipeData(int width, int height) {
setWipeData(width, height, 0xC8000000, 45);
}
/**
* 设置可擦除范围的擦一擦参数
*
* @param width 宽度
* @param height 高度
* @param bgColor 默认背景颜色
* @param strokeWidth 手指滑动的擦除线的宽度
*/
private void setWipeData(int width, int height, int bgColor, int strokeWidth) {
// java.lang.IllegalArgumentException: width and height must be > 0
if (width <= 0 || height <= 0) {
return;
}
// 设置禁用硬件设置
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
// 设置手指画笔
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
// 生成图像手指源目标
// 源--灰色背景区域
decodeResourceSRC = Bitmap.createBitmap(width, height, Config.ARGB_8888);
decodeResourceSRC.eraseColor(bgColor);
// 目标--每次绘制的区域
createBitmapDST = Bitmap.createBitmap(width, height, Config.ARGB_8888);
// 手指滑动的路径
path = new Path();
}
/**
* 绘制擦一擦的擦除效果
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (paint == null || path == null || createBitmapDST == null) {
return;
}
// 分层绘制
int saveLayer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
// 把手指轨迹画到目标图层上
Canvas canvas2 = new Canvas(createBitmapDST);
canvas2.drawPath(path, paint);
// 把目标图像画到画布上
canvas.drawBitmap(createBitmapDST, 0, 0, paint);
// 设置混合模式(当目标图层不透明时,计算结果将是透明的),绘制源图像区域
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
canvas.drawBitmap(decodeResourceSRC, 0, 0, paint);
// 清空混合模式
paint.setXfermode(null);
canvas.restoreToCount(saveLayer);
}
// 使用贝塞尔曲线,使折线过度圆滑
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
// 记录手指触摸的初始化位置
case MotionEvent.ACTION_DOWN:
if (path != null) {
path.moveTo(event.getX(), event.getY());
perX = event.getX();
perY = event.getY();
}
break;
// 记录手指移动的位置
case MotionEvent.ACTION_MOVE:
if (path != null) {
float endX = (perX + event.getX()) / 2;
float endY = (perY + event.getY()) / 2;
path.quadTo(perX, perY, endX, endY);
perX = event.getX();
perY = event.getY();
// 刷新视图
postInvalidate();
}
break;
//手指抬起
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
}