Android Studio实现刮刮卡效果

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

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>

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/67516.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java可执行程序命令】学习路线攻略,史诗级别全汇总 ~

Java可执行程序命令学习路线攻略 &#x1f4d7;文章指路Java可执行命令1、编译工具 javac2、程序启动工具 java3、API文档生成 javadoc4、反编译工具 javap5、打包部署工具 jar6、调试工具 jdb7、C头文件创建 javah8、JWS应用程序启动 javaws9、安装包创建 javapackager10、JAR…

Vue3状态管理库Pinia——自定义持久化插件

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

PostgreSQL和MySQL多维度对比

文章目录 0.前言1. 基础对比2.PostgreSQL和MySQL语法对比3. 特性4. 参考文档 0.前言 在当今的软件开发和数据管理领域&#xff0c;数据库是至关重要的基础设施之一。选择正确的数据库管理系统&#xff08;DBMS&#xff09;对于应用程序的性能、可扩展性和数据完整性至关重要。…

SpringBoot集成Logback日志

SpringBoot集成Logback日志 文章目录 SpringBoot集成Logback日志一、什么是日志二、Logback简单介绍三、SpringBoot项目中使用Logback四、概念介绍一、日志记录器Logger1.1、日志记录器对象生成1.2、记录器的层级结构1.3、过滤器1.4、logger设置日志级别1.5、java代码演示1.6、…

【论文阅读】Deep Instance Segmentation With Automotive Radar Detection Points

基于汽车雷达检测点的深度实例分割 一个区别&#xff1a; automotive radar 汽车雷达 &#xff1a; 分辨率低&#xff0c;点云稀疏&#xff0c;语义上模糊&#xff0c;不适合直接使用用于密集LiDAR点开发的方法 &#xff1b; 返回的物体图像不如LIDAR精确&#xff0c;可以…

API 测试 | 了解 API 接口概念|电商平台 API 接口测试指南

什么是 API&#xff1f; API 是一个缩写&#xff0c;它代表了一个 pplication P AGC 软件覆盖整个房间。API 是用于构建软件应用程序的一组例程&#xff0c;协议和工具。API 指定一个软件程序应如何与其他软件程序进行交互。 例行程序&#xff1a;执行特定任务的程序。例程也称…

Matlab滤波、频谱分析

Matlab滤波、频谱分析 滤波&#xff1a; 某目标信号是由5、15、30Hz正弦波混合而成的混合信号&#xff0c;现需要设计一个滤波器滤掉5、30Hz两种频率。 分析&#xff1a;显然我们应该设计一个带通滤波器&#xff0c;通带频率落在15Hz附近。 % 滤波 % 某目标信号是由5、15、3…

LVS工作环境配置

一、LVS-DR工作模式配置 模拟环境如下&#xff1a; 1台客户机 1台LVS负载调度器 2台web服务器 1、环境部署 &#xff08;1&#xff09;LVS负载调度器 yum install -y ipvsadm # 在LVS负载调度器上进行环境安装 ifconfig ens33:200 192.168.134.200/24 # 配置LVS的VIP…

Mir 2.14 正式发布,Ubuntu 使用的 Linux 显示服务器

导读Canonical 公司最近发布了 Mir 2.14&#xff0c;这是该项目的最新版本。 Mir 2.14 在 Wayland 方面通过 ext-session-lock-v1 协议增加了对屏幕锁定器 (screen lockers) 的支持&#xff0c;并最终支持 Wayland 拖放。此外还整合了渲染平台的实现&#xff0c;放弃了之前在 R…

嵌入式领域:人才供需失衡,发展潜力巨大

嵌入式技术正快速发展&#xff0c;ARM处理器、嵌入式操作系统、LINUX等技术助力嵌入式领域崛起。然而&#xff0c;行业新颖且门槛高&#xff0c;缺乏专业指导。因此&#xff0c;嵌入式人才稀缺&#xff0c;身价水涨船高。 未来几年&#xff0c;嵌入式系统将在信息化、智能化、…

【位操作符的几种题型】

位操作符的几种题型 目录 题型一&#xff1a;寻找“单身狗”。 题型二&#xff1a;计算一个数在二进制中1的个数 题型三&#xff1a;不允许创建临时变量&#xff0c;交换两个整数的内容 题型一&#xff1a;寻找“单身狗”。 1.1题目解析 在一个整型数组中&#xff0c;只有…

【数据结构】‘双向链表’冲冲冲

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Vue脚手架安装

安装包下载 安装包可以去官网下载&#xff08;官网地址&#xff09;&#xff0c;建议下载稳定版。 2. 选择安装目录 选择安装到一个&#xff0c;没有中文&#xff0c;没有空格的目录下&#xff08;新建一个文件夹NodeJS&#xff09; 3. 验证NodeJS环境变量 NodeJS 安装完…

AIGC大模型ChatGLM2-6B:国产版chatgpt本地部署及体验

1 ChatGLM2-6B介绍 ChatGLM是清华技术成果转化的公司智谱AI研发的支持中英双语的对话机器人。ChatGLM基于GLM130B千亿基础模型训练&#xff0c;它具备多领域知识、代码能力、常识推理及运用能力&#xff1b;支持与用户通过自然语言对话进行交互&#xff0c;处理多种自然语言任务…

CNN的特性

1、位移不变性 它指的是无论物体在图像中的什么位置&#xff0c;卷积神经网络的识别结果都应该是一样的。 因为CNN就是利用一个kernel在整张图像上不断步进来完成卷积操作的&#xff0c;而且在这个过程中kernel的参数是共享的。换句话说&#xff0c;它其实就是拿了同一张“通…

徐雷,太委屈

文 | 螳螂观察 作者 | 仲夏 自3月8日上线以来&#xff0c;京东百亿补贴已整整5个月。相比首月投入10个亿&#xff0c;京东百亿补贴如今的存在显得尴尬与鸡肋。 眼看百亿补贴难以肩负发力下沉市场、扛起低价策略的重任&#xff0c;京东近期又将“京喜拼拼”更名“京东拼拼”卷…

RabbitMQ 备份交换机和死信交换机

为处理生产者生产者将消息推送到交换机中&#xff0c;交换机按照消息中的路由键即自身策略无法将消息投递到指定队列中造成消息丢失的问题&#xff0c;可以使用备份交换机。 为处理在消息队列中到达TTL的过期消息&#xff0c;可采用死信交换机进行消息转存。 通过上述描述可知&…

[vue-element-admin]下载与安装

一、环境搭建 1 nodejs 源码地址 sudo apt install build-essential # 内含gcc g make等全家桶git clone git://github.com/nodejs/node.git # 下载源码 cd node sudo ./config sudo make && make install # 编译 node -v # 查看是否编译成功二、遇见的问题 问题…

【606. 根据二叉树创建字符串】

目录 1.题目描述2.算法思想3.代码实现 1.题目描述 这道题的重点其实就是要省去不影响映射的括号。如&#xff1a; 2.算法思想 3.代码实现 class Solution { public:string _tree2str(TreeNode* root,string& ret){if(rootnullptr){return "";}retto_string(ro…

无人车沿着指定线路自动驾驶与远程控制的实践应用

有了前面颜色识别跟踪的基础之后&#xff0c;我们就可以设定颜色路径&#xff0c;让无人车沿着指定线路做自动驾驶了&#xff0c;视频&#xff1a;PID控制无人车自动驾驶 有了前几章的知识铺垫&#xff0c;就比较简单了&#xff0c;也是属于颜色识别的一种应用&#xff0c;主要…