Android Studio实现刮刮卡效果

news2024/10/7 0:11:32

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

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.coloradmin.cn/o/856812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汽车控制器底层软件BOOTLOADER开发经历

现在所谓智能汽车必备的OTA技术&#xff0c;在ECU控制器层面就是BOOT的开发&#xff0c;对应autosar体系里面的BSW基础软件。 同学刚开始接触汽车软件开发会有一种思想&#xff0c;要学就学听起来high level的autosar&#xff0c;但是到底autosar是个什么东西也搞不懂&#xf…

基于数据全生命周期的数据资产价值评估方法及应用

基于数据全生命周期的数据资产价值评估方法及应用 李冬青, 刘吟啸, 邓镭, 李铭洋 阿里巴巴集团&#xff0c;上海 200120 摘要&#xff1a;数据资产价值评估是现代数据资产管理和运营以及数据流通的基础。基于数据全生命周期理论&#xff0c;从第一性原则出发&#xff0c;通过评…

2023好用苹果电脑杀毒软件Cleanmymac X

苹果电脑怎么杀毒&#xff1f;这个问题自从苹果电脑变得越来越普及&#xff0c;苹果电脑的安全性问题也逐渐成为我们关注的焦点。虽然苹果电脑的安全性相对较高&#xff0c;但仍然存在着一些潜在的威胁&#xff0c;比如流氓软件窥探隐私和恶意软件等。那么&#xff0c;苹果电脑…

Day 25 C++ stack容器(栈)

文章目录 stack 基本概念定义基本概念栈顶&#xff08;Top&#xff09;——指向栈中最上面的元素的位置。入栈&#xff08;Push&#xff09;——将元素添加到栈顶。出栈&#xff08;Pop&#xff09;——从栈顶移除元素。栈空&#xff08;Empty&#xff09;——当栈中没有任何元…

企业权限管理(三)-产品添加

产品添加 从product-list.jsp跳转到product-add.jsp <button type"button" class"btn btn-default" title"新建" onclick"location.href${pageContext.request.contextPath}/pages/product-add.jsp"><iclass"fa fa-file…

后端开发9.商品类型模块

概述 简介 商品类型我设计的复杂了点,设计了多级类型 效果图 数据库设计

ORACLE和MYSQL区别

1&#xff0c;Oracle没有offet,limit&#xff0c;在mysql中我们用它们来控制显示的行数&#xff0c;最多的是分页了。oracle要分页的话&#xff0c;要换成rownum。 2&#xff0c;oracle建表时&#xff0c;没有auto_increment&#xff0c;所有要想让表的一个字段自增&#xff0c…

(JS逆向专栏十三)某信平台网站登入SM2

声明: 本文章中所有内容仅供学习交流&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 名称:电信 目标:登入参数 加密类型:SM2 目标网址:https://login.189.cn/web/login …

推出全新TrenchStop™ 5 WR6系列,IKWH50N65WR6XKSA1、IKWH40N65WR6XKSA1带来更佳的系统可靠性(IGBT)

推出全新分立式封装的650V TRENCHSTOP 5 WR6系列&#xff0c;该系列采用TO-247-3-HCC封装&#xff0c;能够实现额定电流分别为20A、30A、40A、50A、60A和70 A的丰富产品组合&#xff0c;可轻松替换前代技术&#xff0c;如TRENCHSTOP 5 WR5、HighSpeed 3 H3技术。该系列针对家用…

Linux驱动之设备树添加蜂鸣器驱动

目录 一、蜂鸣器简介 二、硬件原理分析 三、蜂鸣器驱动原理 四、开发环境 五、修改设备树文件 1、添加 pinctrl 节点 2、添加 BEEP 设备节点 3、检查 PIN 是否被其他外设使用 六、蜂鸣器驱动程序编写 七、测试程序编写 八、运行验证 在 I.MX6U-ALPHA 开发板上有一个有源…

【揽睿星舟】艺术二维码完全生成攻略

导航栏 一、云端平台 1-1、云端平台的优势&#xff1a; 1-2、选择适合的云端平台需要考虑以下几个方面&#xff1a; 二、账号注册界面如下&#xff1a; 三、生成方法 3-1、图像到图像 3-1-1、二维码生成 3-1-2、选择云端平台来启动Stable Diffusion的Web UI 3-1-3、使用S…

记录--使用 JS 实现基本的截图功能

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 思路分析 在开始动手之前&#xff0c;分析一下整个功能的实现过程&#xff1a; 根据图片大小创建 canvas1 画布&#xff0c;并将原图片直接定位在 canvas1 上&#xff1b; 在画布上添加一个蒙层&…

MySQL插入数据库 insert into 语句 用法总结

目录 步骤 一、建表&#xff1a; 二、插入第一行数据 二、插入第二行数据&#xff08;指定要插入字段&#xff09; 三、插入第三行数据&#xff08;指定要插入的字段&#xff0c;但不是所有字段&#xff0c;除了(stu_id, stu_gender)&#xff09; 四、使用一条insert in…

maven中常见问题

文章目录 一、配置项提示二、父子打包三、打包之后不显示target四、自定义打包之后的jar包名称五、整个项目打包5.1、父项目管理插件和微服务打包 一、配置项提示 SpringBoot中提示错误信息 表示的是SpringBoot中的注释提示没有配置&#xff01;那么可以来使用一下springboot官…

【VS Code插件开发】通用功能(二)

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;2022年度博客之星前端领域TOP 2&#xff0c;前端领域优质作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步…

红帽8.2版本CSA题库:第六题创建协作目录权限

红帽认证工程师第六题创建协作目录权限 mkdir /home/managers chown :sysmgrs /home/managers chmod 2770 /home/managers 测试&#xff1a; touch /home/managers/12345 ll /home/managers/12345

【雕爷学编程】Arduino动手做(04)---震动传感器模块5

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

Windows和Linux系统上的矢量运算:指令级并行计算SIMD(SSE/VAX)

注&#xff1a;本文的SIMD&#xff0c;指的是CPU指令架构中的相关概念。不涉及GPU端的算力机制。 基本概念 SIMD&#xff0c;Single Instruction/Multiple Data&#xff0c; 即单指流令多数据流&#xff0c;例如一个乘法指令&#xff0c;可以并行的计算8个浮点数的乘法。 SIM…

【Minecraft】Fabric Mod开发完整流程1 - 环境配置与第一个物品

前言 Fabric 是 Minecraft 一款非官方的模组 API,与 Forge mod 不同。它以轻量级和高性能为设计目标,专注于支持新版本的 Minecraft。 Fabric 和 Forge 在各自的加载编译流程上差别很大&#xff0c;所以你很难看见有同时支持二者的 mod&#xff0c;除非做了兼容性处理 Fabri…

新型高速 JavaScript 运行时 Bun 0.7 发布

导读近日&#xff0c;新型 JavaScript 运行时 Bun 正式发布了 0.7 版本&#xff0c;带来了重大的升级。据悉&#xff0c;Bun 是一个配套齐全的 JavaScript 解决方案&#xff0c;集运行时、打包器、转译器和包管理器于一体&#xff0c;追求极致的运行速度。此次更新主要集中在与…