Android刮刮卡自定义控件

效果图
刮刮卡自定义控件
import android.content.Context;
import android.graphics.Bitmap;
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.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
 * 描述:
 * 作者: shawn
 * 时间: 2024/5/2711:20
 */
public class ScratchView extends View {
    /**
     * 绘制线条的画笔
     */
    private Paint mOutterPaint = new Paint();
    /**
     * 遮层画笔
     */
    private Paint mMaskPaint = new Paint();
    /**
     * 最下面画笔
     */
    private Paint mBackPint = new Paint();
    /**
     * mCanvas绘制内容在其上
     */
    private Bitmap mBitmap;
    /**
     * 记录用户绘制的Path
     */
    private Path mPath = new Path();
    /**
     * 内存中创建的Canvas
     */
    private Canvas mCanvas;
    private boolean isComplete;
    private Rect mTextBound = new Rect();
    private String mText = "¥500,0000";
    private int mLastX;
    private int mLastY;
    private int measuredWidth;
    private int measuredHeight;
    public GuaGuaKaView(Context context) {
        this(context, null);
    }
    public GuaGuaKaView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public GuaGuaKaView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    private void init() {
        mPath = new Path();
        setUpOutPaint();
        setUpBackPaint();
    }
    /**
     * 初始化canvas的绘制用的画笔
     */
    private void setUpBackPaint() {
        mBackPint.setStyle(Paint.Style.FILL);
        mBackPint.setTextScaleX(2f);
        mBackPint.setColor(Color.DKGRAY);
        mBackPint.setTextSize(32);
        mBackPint.getTextBounds(mText, 0, mText.length(), mTextBound);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        if (!isComplete) {
            drawPath();
            canvas.drawBitmap(mBitmap, 0, 0, null);
        } else {
            this.setVisibility(GONE);
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measuredWidth = getMeasuredWidth();//宽高和父view的相同
        measuredHeight = getMeasuredHeight();
        // 初始化bitmap
        mBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
        mMaskPaint.setColor(Color.parseColor("#000000"));//遮层透明
        mMaskPaint.setStyle(Paint.Style.FILL);
        mCanvas.drawRoundRect(new RectF(0, 0, measuredWidth, measuredHeight), 0, 0, mMaskPaint);
//        mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(),
//                R.drawable.bg_withdraw_history), null, new RectF(0, 0, measuredWidth, measuredHeight), null);//遮层
    }
    /**
     * 设置画笔的一些参数
     */
    private void setUpOutPaint() {
        // 设置画笔
//         mOutterPaint.setAlpha(0);
        mOutterPaint.setColor(Color.parseColor("#c0c0c0"));
        mOutterPaint.setAntiAlias(true);
        mOutterPaint.setDither(true);
        mOutterPaint.setStyle(Paint.Style.STROKE);
        mOutterPaint.setStrokeJoin(Paint.Join.ROUND); // 圆角
        mOutterPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角
        // 设置画笔宽度
        mOutterPaint.setStrokeWidth(50);
    }
    /**
     * 绘制线条
     */
    private void drawPath() {
        mOutterPaint.setStyle(Paint.Style.STROKE);
        mOutterPaint
                .setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//取俩者的交集
        mCanvas.drawPath(mPath, mOutterPaint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = Math.abs(x - mLastX);
                int dy = Math.abs(y - mLastY);
                if (dx > 3 || dy > 3)
                    mPath.lineTo(x, y);
                mLastX = x;
                mLastY = y;
                new Thread(mRunnable).start();
                break;
            case MotionEvent.ACTION_UP:
                new Thread(mRunnable).start();
                break;
        }
        invalidate();
        return true;
    }
    /**
     * 统计擦除区域任务
     */
    private Runnable mRunnable = new Runnable() {
        private int[] mPixels;
        @Override
        public void run() {
            int w = getWidth();
            int h = getHeight();
            float wipeArea = 0;
            float totalArea = w * h;
            Bitmap bitmap = mBitmap;
            mPixels = new int[w * h];
            /**
             * 拿到所有的像素信息
             */
            bitmap.getPixels(mPixels, 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;
                    if (mPixels[index] == 0) {
                        wipeArea++;
                    }
                }
            }
            /**
             * 根据所占百分比,进行一些操作
             */
            if (wipeArea > 0 && totalArea > 0) {
                int percent = (int) (wipeArea * 100 / totalArea);
                Log.e("TAG", "清除区域 = " + percent);
                if (percent > 75) {
                    isComplete = true;
                    postInvalidate();
                }
            }
        }
    };
    /**
     * 将布局转换成bitmap
     * @param addViewContent
     * @return
     */
    private Bitmap getViewBitmap(View addViewContent) {
        addViewContent.setDrawingCacheEnabled(true);
        addViewContent.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        addViewContent.layout(0, 0,
                addViewContent.getMeasuredWidth(),
                addViewContent.getMeasuredHeight());
        addViewContent.buildDrawingCache();
        Bitmap cacheBitmap = addViewContent.getDrawingCache();
        Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
        return bitmap;
    }
}
 如何使用?
<RelativeLayout>
<!-- 显示层 -->
<ImageView
            android:id="@+id/iv_holder"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="14dp"
            android:layout_marginTop="60dp"
            android:layout_marginEnd="14dp"
            android:scaleType="fitXY"
            android:src="@drawable/bg_drama_top"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
<!-- 刮奖层 -->
<ScratchView
            app:layout_constraintTop_toTopOf="@id/iv_holder"
            app:layout_constraintBottom_toBottomOf="@id/iv_holder"
            app:layout_constraintStart_toStartOf="@id/iv_holder"
            app:layout_constraintEnd_toEndOf="@id/iv_holder"
            android:layout_width="0dp"
            android:layout_height="0dp"/>
</RelativeLayout>

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

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

相关文章

基于SpringBoot设计模式之结构型设计模式·适配器模式

文章目录 介绍开始使用委托的适配器&#xff08;媒体播放器&#xff09;架构图定义被适配者定义需求接口定义适配者 使用继承的适配器&#xff08;手机充电接口&#xff09;架构图定义被适配者定义需求接口定义适配者 测试样例 总结优点缺点 介绍 在程序世界中&#xff0c;经常…

4,八种GPIO模式

资料来源:【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 仅作个人自学笔记&#xff0c;如有冒犯&#xf…

版本匹配指南:PyTorch版本、Python版本和pytorch_lightning版本的对应关系

版本匹配指南&#xff1a;PyTorch版本、Python版本和pytorch_lightning版本的对应关系 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a; 我是高斯…

《征服数据结构》双向链表

摘要&#xff1a; 1&#xff0c;双链表的介绍 2&#xff0c;双链表的用途 3&#xff0c;双链表的节点插入和删除 1&#xff0c;双链表的介绍 前面我们讲过单链表&#xff0c;单链表的特点就是只能往后访问不能往前访问。单链表一般在面试中用的比较多&#xff0c;比如删除倒数第…

Ovid医学库文献如何在家查找下载

今天讲的数据库是一个知名医学库——Ovid Ovid隶属于威科集团的健康出版事业集团&#xff0c;与LWW、Adis等公司属于姊妹公司。Ovid数据库在医学外文文献数据库方面占据绝对地位&#xff0c;目前已有包涵人文、科技等多领域数据库300个&#xff0c;其中80多个是生物医学数据库…

Web组态可视化编辑器 快速绘制组态图

演示地址&#xff1a;by组态[web组态插件] 随着工业智能制造的发展&#xff0c;工业企业对设备可视化、远程运维的需求日趋强烈&#xff0c;传统的单机版组态软件已经不能满足越来越复杂的控制需求&#xff0c;那么实现Web组态可视化界面成为了主要的技术路径。 行业痛点 对于…

Ps 滤镜:消失点

Ps菜单&#xff1a;滤镜/消失点 Filter/Vanishing Point 快捷键&#xff1a;Ctrl Alt V 两条平行的铁轨或两排树木连线相交于很远很远的某一点&#xff0c;这点在透视图中叫做“消失点”&#xff0c;也称为“灭点”。 消失点 Vanishing Point滤镜主要用于在图像中处理具有透视…

NSS题目练习4

[LitCTF 2023]1zjs 打开后是一个游戏&#xff0c;用dirsearch扫描&#xff0c;什么都没发现 查看源代码搜索flag&#xff0c;发现没有什么用 搜索php&#xff0c;访问 出现一堆符号&#xff0c;看样子像是jother编码 解码得到flag&#xff0c;要删掉[] [LitCTF 2023]Http pro …

【StableDiffusion】SD1.4、1.5、2.0、2.1 和 SDXL0.9-1.0、SDXL turbo 等的区别

总览 1.基础sd base model家族&#xff1a;SD1.4、SD1.5、SD1.5-LCM、SD2.0、SD2.0-768、SD2.1、SD2.1-768、SD2.1-UNCLIP 2.升级sdxl base model家族&#xff1a;SDXL0.9、SDXL1.0、SDXL1.0-LCM、SDXL-DISTILLED、SDXL-TURBO 3.专门用于视频生成的 SVD 家族&#xff1a;SVD、…

备战秋招c++ 【持续更新】

T1 牛牛的快递 原题链接&#xff1a;牛牛的快递_牛客题霸_牛客网 (nowcoder.com) 题目类型&#xff1a;模拟 审题&确定思路&#xff1a; 1、超过1kg和不足1kg有两种不同收费方案 ---- 起步价问题 2、超出部分不足1kg的按1kg计算 ----- 向上取整 3、向上取整的实现思路…

卷出新高度,直呼太强!时隔三月,YOLO再度进化升级:《YOLOv10—实时端到端目标检测》重磅来袭

真的是不止一次感叹&#xff0c;学习的速度都跟不上发论文出新品的速度。。。。。 继前文YOLOv9发布以来也就不到三个月的时间&#xff0c;YOLOv10就来了&#xff01; 《太卷了&#xff0c;目标检测新成员——YOLOv9: Learning What You Want to LearnUsing Programmable Gra…

城市空气质量数据爬取分析可视化

城市空气质量数据爬取分析可视化 一、效果展示二、完整代码2.1 数据爬取代码2.2 数据分析代码一、效果展示 先来看一下数据情况以及可视化效果,本项目使用了pyecharts绘制了日历图、雷达图、折线图、柱状图、饼图和平行坐标系。完整代码附后: 数据如下: 日历图: 饼图: …

拿捏数据结构-top_k问题

top_k问题时间复杂度的计算 这里提前说明&#xff0c;时间复杂度的计算的目的是来计算向上调整的更优还是向下调整更优&#xff0c;从肉眼看的话向下调整优于向上调整&#xff0c;接下来我们进行时间复杂度的计算。 此时我们会用到等比数列求和以及裂项相消 如图 首先我们假设求…

Github Actions/workflow的使用

背景 Github提供了免费的Actions执行workflows工作流&#xff0c;在CI/CD场景下可用于跑测试用例、构建、打包、部署/发版等操作。 使用介绍 工作流简介 1个project可以配置多个workflow&#xff0c;每个workflow使用一个yaml文件配置&#xff1b;单个workflow可以配置多个…

身份认证页面该怎么设计更加合理?

一、认证页面的作用 认证页面在应用程序中具有以下几个重要的作用&#xff1a; 验证用户身份&#xff1a;认证页面的主要作用是验证用户的身份。通过要求用户提供正确的凭据&#xff08;如用户名和密码、生物特征、验证码等&#xff09;&#xff0c;认证页面可以确认用户是合法…

使用华为快传同步文件至电脑

使用华为快传同步文件至电脑&#xff0c;电脑端未发现设备解决办法 1、手机和电脑连同一网络 2、打开手机华为分享&#xff0c;打开电脑网络 3、网络中找到设备&#xff0c;输入账户密码进行连接&#xff08;未找到设备往下继续看&#xff09; 未找到设备解决办法&#xff1…

图解 Transformer

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

拿捏数据结构- 链式二叉树

链式二叉树的概念&#xff1a; 链式二叉树解决的是非完全二叉树解决不了的问题 什么意思呢&#xff0c;简单的说就是&#xff0c;链式二叉树 可以是下面三种二叉树 但是非链式二叉树只能是前两种 链式二叉树的存储 节点结构&#xff1a;首先定义一个结构体或类来表示二叉树的节…

Java跨Docker容器备份数据库数据

Java跨Docker容器备份数据库数据 前置背景思路整理编写备份脚本容器启动检验效果Java容器MySQL容器 Java代码执行备份 我的个人博客&#xff1a;Lichg&#xff0c;欢迎大家访问。 前置背景 在我们的开发部署场景中&#xff0c;通常多数使用Docker进行部署。当你的数据库和项目…

Ubuntu22.04之扩展并挂载4T硬盘(二百三十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…