我最近悟出了一个道理:“学习感觉时间过的很慢,玩游戏感觉时间过的很快,因为学习让我度日如年啊!想要活的长的话要多学习···
不光如此,学习还治好了我的失眠症,我看书一会就瞌睡了。” 所以:学习是个好东西。
按照惯例先上效果
原理
我们可以把这个刮刮卡看成有两个图层
- 开奖结果层
- 遮罩层
遮罩层其实也是有两个图层组成
- 盖着结果的红色图层
- 手指划过的路径层
遮罩层是由上面两个图层组成,两个图层想叠加的地方会显示出来。我们理由Paint设置图形混合模式来实现遮罩效果:PorterDuffXfermode图形混合模式
知识点
- Paint
- Path
- Canvas
- Paint.setXfermode 图像混合模式的设置 来实现遮罩效果
代码
public class ScratchCardView extends View {
private Paint mPaint;
private Bitmap mDstBmp, mSrcBmp, mTxtBmp;
private Path mPath;
public ScratchCardView(Context context) {
this(context, null);
}
public ScratchCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//初始化画笔
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(80);
//禁用硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
//初始化图片对象
mTxtBmp = BitmapFactory.decodeResource(getResources(), R.drawable.result);
mSrcBmp = BitmapFactory.decodeResource(getResources(), R.drawable.eraser);
mDstBmp = Bitmap.createBitmap(mSrcBmp.getWidth(), mSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);
//路径(贝塞尔曲线)
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制刮奖结果
canvas.drawBitmap(mTxtBmp, 0, 0, mPaint);
//使用离屏绘制
int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
//先将路径绘制到 bitmap上
Canvas dstCanvas = new Canvas(mDstBmp);
dstCanvas.drawPath(mPath, mPaint);
//绘制 目标图像
canvas.drawBitmap(mDstBmp, 0, 0, mPaint);
//设置 模式 为 SRC_OUT, 擦橡皮区域为交集区域需要清掉像素
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));//这里实现来遮罩,图层顺序不能乱
//绘制源图像
canvas.drawBitmap(mSrcBmp, 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerID);
}
private float mEventX, mEventY;
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mEventX = event.getX();
mEventY = event.getY();
mPath.moveTo(mEventX, mEventY);
break;
case MotionEvent.ACTION_MOVE:
float endX = (event.getX() - mEventX) / 2 + mEventX;
float endY = (event.getY() - mEventY) / 2 + mEventY;
//画二阶贝塞尔曲线
mPath.quadTo(mEventX, mEventY, endX, endY);
mEventX = event.getX();
mEventY = event.getY();
break;
}
invalidate();
return true; //消费事件
}
}
在Activity中如下调用即可运行
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new ScratchCardView(this));
}
}
是不是很简单
源码下载
在MileAndroid/CustomVIew/CustomViewGroup/文件夹下面
下面介绍写图形混合模式的各种类型
//所绘制不会提交到画布上
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),
//取两图层全部区域,交集部分饱和度相加
new PorterDuffXfermode(PorterDuff.Mode.ADD),
//取两图层全部区域,交集部分叠加
new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
官方demo展示效果如下
资源下载:csdn资源
需要共享分,如果没有共享分可以去github
上下载
github地址:https://github.com/rain86/MileAndroid
在如下图CustomView目录下