Android Studio实现刮刮卡效果

代码和刮刮乐图片参考网络

实现效果

MainActivity

java 复制代码
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

java 复制代码
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

java 复制代码
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

java 复制代码
<?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>

遮盖图

相关推荐
木头没有瓜1 分钟前
vscode离线安装插件
ide·vue.js·vscode
小蜜蜂嗡嗡1 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi001 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
lls2331 小时前
恢复idea文件目录
ide
zhangphil3 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你3 小时前
Android View的绘制原理详解
android
NeRF_er3 小时前
使用 VScode Debug加不上断点的原因
ide·vscode·编辑器
GHL2842710903 小时前
error MSB8041: 此项目需要 MFC 库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集和体系结构安装它们。
ide·visual studio
移动开发者1号6 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号6 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin