代码和刮刮乐图片参考网络
实现效果
MainActivity
import android.app.Activity;
import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}
ScratchCardView
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;public class ScratchCardView extends View {//类成员变量private Paint mPaint;//画笔private Path mPath;//手指滑动的路径private Canvas mCanvas;//临时画布private Bitmap mBackGroundBitmap;//未刮奖前背景private Bitmap mForeGroundBitmap;//前景图(灰色)private int mLastX, mLastY;//滑动结束点的坐标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.setAlpha(0);//设置alpha不透明度,范围为0~255mPaint.setAntiAlias(true);//消除锯齿边,给画笔设置平滑的属性mPaint.setStyle(Paint.Style.STROKE);//描边效果mPaint.setStrokeCap(Paint.Cap.ROUND);//圆角效果mPaint.setStrokeJoin(Paint.Join.ROUND);//设置圆角mPaint.setStrokeWidth(20);//设置画笔宽度mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//设置图层混合模式mPath = new Path();// 实例化路径//未刮奖前背景 图片资源转化为BitmapmBackGroundBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.background);//创建一个和背景图大小一致的Bitmap对象作为装载画布mForeGroundBitmap = Bitmap.createBitmap(mBackGroundBitmap.getWidth(), mBackGroundBitmap.getHeight(), Config.ARGB_8888);//与Canvas进行绑定 //画涂层的画布,传一个Bitmap进去,所画的信息都存在Bitmap上mCanvas = new Canvas(mForeGroundBitmap);//涂成灰色mCanvas.drawColor(Color.BLUE);}@Overrideprotected void onDraw(Canvas canvas) {//先把底层的画画到View的画布上canvas.drawBitmap(mBackGroundBitmap, 0, 0, null);//绘制前景层canvas.drawBitmap(mForeGroundBitmap, 0, 0, null);}/*** 手指滑动事件处理,把手指移动的轨迹保存在Path中.* 不停的移动,就不停的回调View的更新UI的方法:invalidate();*/@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {//当用户按下屏幕时,会执行MotionEvent.ACTION_DOWN的case分支,// 记录下当前的坐标,并将路径(Path)移动到该点case MotionEvent.ACTION_DOWN:mLastX = (int) event.getX();mLastY = (int) event.getY();mPath.moveTo(mLastX, mLastY);break;//当用户在屏幕上滑动时,会执行MotionEvent.ACTION_MOVE的case分支,// 记录下当前的坐标,并将路径绘制到该点case MotionEvent.ACTION_MOVE:mLastX = (int) event.getX();mLastY = (int) event.getY();mPath.lineTo(mLastX, mLastY);break;//当用户松开屏幕时,会执行MotionEvent.ACTION_UP的case分支,不做任何操作。case MotionEvent.ACTION_UP:break;default:break;}mCanvas.drawPath(mPath, mPaint);//将路径绘制到画布上invalidate();//调用invalidate()方法刷新视图return true;//表示已经处理了触摸事件}
}
ScratchCardView2
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;public class ScratchCardView2 extends View {//处理文字private String mText = "恭喜您中奖啦!!";//刮奖文本信息private Paint mTextPaint;//文字画笔private Rect mRect;//用于表示坐标系中的一块矩形区域//处理图层private Paint mForePaint;//画笔private Path mPath;//手指滑动的路径private Bitmap mBitmap;//加载资源文件private Canvas mForeCanvas;//前景图Canvasprivate Bitmap mForeBitmap;//前景图Bitmap//记录位置private int mLastX;private int mLastY;private volatile boolean isClear;//标志是否被清除public ScratchCardView2(Context context) {this(context, null);}public ScratchCardView2(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ScratchCardView2(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mRect = new Rect();//实例化矩形区域mPath = new Path();//实例化画笔的路径//文字画笔mTextPaint = new Paint();//初始化画笔mTextPaint.setAntiAlias(true);//消除锯齿边,给画笔设置平滑的属性mTextPaint.setColor(Color.BLACK);//文字颜色mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);//描边效果mTextPaint.setTextSize(50);//字体大小//用于测量文本边界的方法。这个方法接受四个参数://mText 是要测量的文本字符串,0 是文本开始的位置,mText.length() 是文本的长度,mRect 是用于存储测量结果的矩形。mTextPaint.getTextBounds(mText, 0, mText.length(), mRect);//擦除画笔mForePaint = new Paint();mForePaint.setAntiAlias(true); //消除锯齿边,给画笔设置平滑的属性mForePaint.setAlpha(0); //设置alpha不透明度,范围为0~255mForePaint.setStrokeCap(Paint.Cap.ROUND);//圆角效果mForePaint.setStrokeJoin(Paint.Join.ROUND);//设置圆角mForePaint.setStyle(Paint.Style.STROKE);//描边效果mForePaint.setStrokeWidth(50);//设置画笔宽度mForePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//设置图层混合模式//在相交时利用源图像的透明度来改变目标图像的透明度和饱和度的,也就是当源图像透明度为0时,目标图像完全不显示//通过资源文件创建Bitmap对象 图片资源转化为BitmapmBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.background);//创建一个和背景图大小一致的Bitmap对象作为装载画布mForeBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);//双缓冲,装载画布//与Canvas进行绑定 //画涂层的画布,传一个Bitmap进去,所画的信息都存在Bitmap上mForeCanvas = new Canvas(mForeBitmap);//将前景图画到View的画布上mForeCanvas.drawBitmap(mBitmap, 0, 0, null);}@Overrideprotected void onDraw(Canvas canvas) {//canvas.drawText()方法绘制文本,这个方法接收四个参数:// 要绘制的文本字符串 mText,// 文本的水平位置 mForeBitmap.getWidth() / 2 - mRect.width() / 2,// 文本的垂直位置 mForeBitmap.getHeight() / 2 + mRect.height() / 2,// 以及用于绘制文本的画笔对象 mTextPaintcanvas.drawText(mText, mForeBitmap.getWidth() / 2 - mRect.width() / 2, mForeBitmap.getHeight() / 2 + mRect.height() / 2, mTextPaint);//如果 isClear 为 false,则使用 canvas.drawBitmap() 方法绘制位图。//方法接收三个参数:要绘制的位图对象 mForeBitmap,位图在画布上的水平位置 0,位图在画布上的垂直位置 0if (!isClear) {canvas.drawBitmap(mForeBitmap, 0, 0, null);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {//当用户按下屏幕时,会执行MotionEvent.ACTION_DOWN的case分支,// 记录下当前的坐标,并将路径(Path)移动到该点case MotionEvent.ACTION_DOWN:mLastX = (int) event.getX();mLastY = (int) event.getY();mPath.moveTo(mLastX, mLastY);break;//当用户在屏幕上滑动时,会执行MotionEvent.ACTION_MOVE的case分支,// 记录下当前的坐标,并将路径绘制到该点case MotionEvent.ACTION_MOVE:mLastX = (int) event.getX();mLastY = (int) event.getY();mPath.lineTo(mLastX, mLastY);break;//当用户松开屏幕时,会执行MotionEvent.ACTION_UP的case分支,不做任何操作。case MotionEvent.ACTION_UP:new Thread(mRunnable).start();break;default:break;}mForeCanvas.drawPath(mPath, mForePaint);//将路径绘制到画布上invalidate();//调用invalidate()方法刷新视图return true;//表示已经处理了触摸事件}/*** 开启子线程计算被擦除的像素点*/private Runnable mRunnable = new Runnable() {int[] pixels;// 这段代码的作用是计算位图中透明像素的擦拭面积,// 并根据擦拭面积占总面积的比例判断是否达到清除条件,如果达到则刷新视图。@Overridepublic void run() {//获取mForeBitmap的宽和高int w = mForeBitmap.getWidth();int h = mForeBitmap.getHeight();float wipeArea = 0;//擦拭面积float totalArea = w * h;//总面积pixels = new int[w * h];/*** pixels 接收位图颜色值的数组* offset 写入到pixels[]中的第一个像素索引值* stride pixels[]中的行间距个数值(必须大于等于位图宽度)。可以为负数* x 从位图中读取的第一个像素的x坐标值。* y 从位图中读取的第一个像素的y坐标值* width 从每一行中读取的像素宽度* height 读取的行数*///获取位图像素数据存储到数组中,mForeBitmap 是一个位图对象,//pixels 是一个用于存储像素数据的数组。w 和 h 分别表示要获取的像素数据的宽度和高度。这个方法将指定区域的位图像素数据存储到 pixels 数组中。mForeBitmap.getPixels(pixels, 0, w, 0, 0, w, h);//使用两层循环遍历位图的每个像素for (int i = 0; i < w; i++) {for (int j = 0; j < h; j++) {int index = i + j * w;//判断像素的颜色值是否为0(即透明像素),如果是,则将擦拭面积wipeArea加1。if (pixels[index] == 0) {wipeArea++;}}}//在循环结束后,通过判断擦拭面积和总面积是否大于0,计算出擦拭面积占总面积的百分比。//如果擦拭面积百分比大于50%,则将变量isClear置为true,表示达到了清除条件。//最后调用postInvalidate()方法刷新视图。if (wipeArea > 0 && totalArea > 0) {int percent = (int) (wipeArea * 100 / totalArea);if (percent > 50) {isClear = true;postInvalidate();}}}};
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.guaguale.ScratchCardView2android:id="@+id/scratchCardView"android:layout_width="200dp"android:layout_height="200dp"android:layout_centerVertical="true"android:layout_centerHorizontal="true" />
</RelativeLayout>
遮盖图