仿制支付宝刮刮卡
2017-11-10 本文已影响194人
剑小河
跟支付宝那个刮刮卡差不多,体验可能更好点(个人以为)
先看图:
这是刮开的效果图
刮开.gif
看起来还可以吧
当刮开面积超过70%的时候,显示全部底图
显示奖励.gif
使用
xml
<com.riverlet.scratchcard.ScratchCardView
android:id="@+id/scratch_card_01"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:layout_marginTop="5dp" />
activity
ScratchCardView scratchCardView = findViewById(R.id.scratch_card_01);
scratchCardView.setDownLayer(R.drawable.down);//设置谜底
scratchCardView.setUpLayer(R.drawable.up);//设置遮盖层
//这是刮开谜底完成的回调
scratchCardView.setOnCompleteListener(new ScratchCardView.OnCompleteListener() {
@Override
public void onComplete() {
Log.d(TAG,"完成00");
}
});
setDownLayer和setUpLayer还支持文字、颜色和Bitmap
---------------------只使用看到这里就可以了----------------------------------
刮卡需要实现的基本代码
Bitmap canvasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个Bitmp
Canvas scratchCanvas = new Canvas(canvasBitmap);//初始化画布
RectF rectF = new RectF(0, 0, width, height);//初始化画布和遮盖层绘制的位置
scratchCanvas.drawBitmap(upLayer.bitmap, null, rectF, null);//把还在遮盖绘制到画布上
Paint pathPaint = new Paint();
pathPaint.setColor(Color.WHITE);//颜色随意,只要不是透明
pathPaint.setStrokeWidth(50);
pathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//这里是重点
scratchCanvas.drawPath(path, pathPaint);//把path绘制到画布上,这条遮盖层会被消除
//然后在onDraw或者dispatchDraw使用view的Canvas绘制这个canvasBitmap 就可以了
Coding
绘制
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (rectF != null) {
//绘制下面的图层
drawDownLayer(canvas);
//绘制上面的图层
if (!isComplete) {
canvas.drawBitmap(canvasBitmap, null, rectF, null);
}
}
}
直接在dispatchDraw方法中绘制即可,不过我这里为了方便多种类型的绘制,做了一下封装,根据文字,颜色和图片分类绘制
绘制下面的图层比较简单
private void drawDownLayer(Canvas canvas) {
if (downLayer != null) {
switch (downLayer.type) {
case TYPE_BITMAP:
canvas.drawBitmap(downLayer.bitmap, null, rectF, null);
break;
case TYPE_COLOR:
canvas.drawColor(downLayer.color);
break;
case TYPE_TEXT:
canvas.drawColor(downLayer.color);
canvas.drawText(downLayer.text, downLayer.textStartX, downLayer.textStartY, downLayer.paint);
break;
}
}
}
绘制上面的图层
绘制上面的额图层就比较麻烦了,先创建一个跟View相同大小的Bitmap,使用它作为Canvas操作的对象,先把上面的图(遮盖层)绘制到上面,
private void initCanvas(int width, int height) {
//初始化画布
canvasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
scratchCanvas = new Canvas(canvasBitmap);
rectF = new RectF(0, 0, width, height);
if (downLayer.type == TYPE_TEXT) {
measureDownText();
}
drawUpLayer();
}
初始化一个Path记录手机移动的位置,初始化Path的Paint
pathPaint = new Paint();
pathPaint.setColor(Color.WHITE);//颜色随意,只要不是透明
pathPaint.setAntiAlias(true);
pathPaint.setDither(true);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeJoin(Paint.Join.ROUND); // 圆角
pathPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角
pathPaint.setStrokeWidth(50);
pathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//这里是重点
//初始化path
path = new Path();
关于Xfermode,这里配上经典说明图一张
Xfermode.jpg
也就是path的Paint的Xfermode类型是DST_OUT的情况,path所绘制的区域都会变成透明
重写onTouchEvent,记录使用path记录手指轨迹,并绘制在我们创建的画布上(scratchCanvas )
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) (event.getX());
int y = (int) (event.getY());
int offsetX = (int) (rectF != null ? rectF.left : 0);
int offsetY = (int) (rectF != null ? rectF.top : 0);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
path.moveTo(x - offsetX, y - offsetY);
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int dx = Math.abs(x - mLastX);
int dy = Math.abs(y - mLastY);
if (dx > 3 || dy > 3) {
//贝塞尔曲线
path.quadTo(mLastX - offsetX, mLastY - offsetY, (x + mLastX) / 2 - offsetX, (y + mLastY) / 2 - offsetY);
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
computeScratchArea();
break;
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
computeScratchArea();
break;
}
drawPath();
invalidate();
return true;
}
private void drawPath() {
if (scratchCanvas != null) {
scratchCanvas.drawPath(path, pathPaint);
}
}
计算刮开的面积
即就死按画布上透明像素所占比例,这里思路源于洋神,洋神无敌
private void computeScratchArea() {
if (!isComplete) {
executorService.execute(runnable);
} else {
executorService.shutdownNow();
}
}
//在子线程计算
private Runnable runnable = new Runnable() {
@Override
public void run() {
if (isComplete) {
return;
}
Bitmap bitmap = canvasBitmap;
int w = getWidth();
int h = getHeight();
int[] pixels = new int[w * h];
float wipeArea = 0;
float totalArea = w * h;
//获取像素数据
bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
//遍历色素值为0的像素,也就是透明区域
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int index = i + j * w;
if (pixels[index] == 0) {
wipeArea++;
}
}
}
if (wipeArea > 0 && totalArea > 0) {
int percent = (int) (wipeArea * 100 / totalArea);
Log.e("TAG", percent + "");
if (percent > 70 && !isComplete) {
isComplete = true;
postInvalidate();
Log.d(TAG, "........." + isComplete);
if (onCompleteListener != null) {
post(new Runnable() {
@Override
public void run() {
onCompleteListener.onComplete();
}
});
}
}
}
}
};
描述能力有限,看不懂的小伙伴,直接看代码:代码地址