MainActivity
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected 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~255
mPaint.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();// 实例化路径
//未刮奖前背景 图片资源转化为Bitmap
mBackGroundBitmap = 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);
}
@Override
protected void onDraw(Canvas canvas) {
//先把底层的画画到View的画布上
canvas.drawBitmap(mBackGroundBitmap, 0, 0, null);
//绘制前景层
canvas.drawBitmap(mForeGroundBitmap, 0, 0, null);
}
/**
* 手指滑动事件处理,把手指移动的轨迹保存在Path中.
* 不停的移动,就不停的回调View的更新UI的方法:invalidate();
*/
@Override
public 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;//前景图Canvas
private 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~255
mForePaint.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对象 图片资源转化为Bitmap
mBitmap = 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);
}
@Override
protected void onDraw(Canvas canvas) {
//canvas.drawText()方法绘制文本,这个方法接收四个参数:
// 要绘制的文本字符串 mText,
// 文本的水平位置 mForeBitmap.getWidth() / 2 - mRect.width() / 2,
// 文本的垂直位置 mForeBitmap.getHeight() / 2 + mRect.height() / 2,
// 以及用于绘制文本的画笔对象 mTextPaint
canvas.drawText(mText, mForeBitmap.getWidth() / 2 - mRect.width() / 2, mForeBitmap.getHeight() / 2 + mRect.height() / 2, mTextPaint);
//如果 isClear 为 false,则使用 canvas.drawBitmap() 方法绘制位图。
//方法接收三个参数:要绘制的位图对象 mForeBitmap,位图在画布上的水平位置 0,位图在画布上的垂直位置 0
if (!isClear) {
canvas.drawBitmap(mForeBitmap, 0, 0, null);
}
}
@Override
public 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;
// 这段代码的作用是计算位图中透明像素的擦拭面积,
// 并根据擦拭面积占总面积的比例判断是否达到清除条件,如果达到则刷新视图。
@Override
public 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.ScratchCardView2
android:id="@+id/scratchCardView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>