Android Studio实现刮刮卡效果

news/2025/2/22 20:17:47

代码和刮刮乐图片参考网络
实现效果
在这里插入图片描述
在这里插入图片描述

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>

遮盖图
在这里插入图片描述


http://www.niftyadmin.cn/n/4926885.html

相关文章

【Stable Diffusion】雨天、湿身

一、Models 1.1、Wet Clothes (Clothing Style) [LoHA] WECL SEE-THROUGH WET WET HAIR BIKINI OR SWIMSUIT UNDER CLOTHES NO BRA BRA VISIBLE THROUGH CLOTHES MISC SHIRTS MISC CLOTHES1.2、Rain 雨 Multiply Style rain style1.3、Wet T-Shirt LORA <lora:wetshirt:…

TS学习07-类型推论

类型推论 ts在没有明确指出类型的地方&#xff0c;类型推论会帮助提供类型 let x 3;推断发生在初始化变量和成员&#xff0c;设置默认参数值和决定函数的返回值时。 最佳通用类型 计算通用类型算法-会考虑所有候选类型 给出一个兼容所有类型的的类型 let x [0,1,null,1…

【安卓串口通信】

安卓串口通信需要使用到串口适配器和USB OTG线。首先需要在Android设备上安装串口调试助手或其他支持串口通信的应用程序。然后将串口适配器连接到Android设备&#xff0c;使用USB OTG线连接即可。 接下来&#xff0c;您需要打开串口调试助手或其他应用程序&#xff0c…

视频分辨率: UXGA/SVGA/VGA/QVGA/QQVGA

视频分辨率除了常见的720p/2K/4K外, 还有VGA系列的分辨率 相关字段含义: V——Video &#xff08;视频&#xff09; G——Graphics&#xff08;图像&#xff09; A——Array&#xff08;阵列&#xff09; S——Super(超级) X——Extended(扩展) U——Ultra(终极) W——Wide&am…

RWEQ风蚀方程模型与ArcGIS数据处理Python代码库添加结合理论研究和科研实践

RWEQ模型是应用比较普遍的能适应大区域定量估算风蚀量的模型。该模型是基于大量野外实验的一种经验模型&#xff0c;在实际测定风力导致的土壤侵蚀量以及当地的气象、地表植被、土壤湿度、地表的结皮和地表的可蚀性等因子的基础上得出的一个经验方程。 1、掌握土壤风蚀模型的原…

vscode vue3+vite 配置eslint

vue2webpackeslint配置 目前主流项目都在使用vue3vite&#xff0c;因此针对eslint的配置做了一下总结。 引入ESlint、pritter 安装插件&#xff0c;执行以下命令 // eslint // prettier // eslint-plugin-vue // eslint-config-prettier // eslint-plugin-prettier yarn ad…

vue3 - 使用reactive定义响应式数据进行列表赋值时,视图没有更新的解决方案

文章目录 1&#xff0c;问题2&#xff0c;原因3&#xff0c;解决方案一、再封装一层数据&#xff0c;即定义属性名&#xff0c;在后期赋值的时候&#xff0c;对此属性进行直接赋值三、使用数组的splice来直接更改原数组三、使用 ref 来定义数据 1&#xff0c;问题 在Vue 3.0 中…

使用AI工具Lama Cleaner一键去除水印、人物、背景等图片里的内容

使用AI工具Lama Cleaner一键去除水印、人物、背景等图片里的内容 前言前提条件相关介绍Lama Cleaner环境要求安装Lama Cleaner启动Lama CleanerCPU方式启动GPU方式启动 使用Lama Cleaner测试结果NO.1 检测框NO.2 水印NO.3 广州塔NO.4 人物背景 参考 前言 由于本人水平有限&…