跟支付宝那个刮刮卡差不多,体验可能更好点(个人以为)
先看图:
这是刮开的效果图
使用
xml
复制代码
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,这里配上经典说明图一张
重写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(); } }); } } } } };复制代码
描述能力有限,看不懂的小伙伴,直接看代码: