Android通过自定义view实现刮刮乐效果详解

 更新时间:2022年03月29日 08:55:33   作者:吐尔洪江Coding  
这篇文章主要介绍了如何在Android中利用自定义的view实现刮刮乐的效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟上小编一起动手试一试

前言

已经有两个月没有更新博客了,其实这篇文章我早在两个月前就写好了,一直保存在草稿箱里没有发布出来。原因是有一些原理性的东西还没了解清楚,最近抽时间研究了一下混合模式,终于也理解了刮刮乐是怎么实现的,所以想继续分享一下自己的一些心得,先上效果图。

效果图:

实现原理

其实刮刮乐实现原理也不算很复杂,最关键的还是需要了解Paint的混合模式。因为刮刮乐是由两个bitmap组成的,一个是源图另一个是目标图,我们需要把目标图的颜色改成灰色,在源图上面盖上了一张灰色的目标图。当手指滑动屏幕时paint会在新的canvas上画出路径,由于新的canvas会持有一个新的bitmap,最终两个bitmap的像素点重叠时就显示源图的部分,从而实现了刮刮乐的效果。这里用的是混合模式中的PorterDuff.Mode.DST_IN。

关键代码:

pathPaint.setXfermode(new PorterDuffXfermode((PorterDuff.Mode.DST_IN)));

关键步骤

创建bitmap

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmapBackground = getBitmap(mBitmapBackground, w, h);
        mBitmapFront = Bitmap.createBitmap(mBitmapBackground.getWidth(),
                mBitmapBackground.getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas.setBitmap(mBitmapFront);
        drawText(mCanvas, w, h);
    }

onSizeChanged方法里面创建了两个bitmap,一个是背景图,另一个是覆盖在背景图上面的bitmap。然后是在上面的bitmap上面绘制文字。

绘制文字

    private void drawText(Canvas canvas, int mWidth, int mHeight) {
        String text = "赶紧刮开吧";
        canvas.drawColor(Color.GRAY);
        Paint.FontMetrics fm = mPaintText.getFontMetrics();
        int mTxtWidth = (int) mPaintText.measureText(text, 0, text.length());
        int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
        int x = mWidth / 2 - mTxtWidth / 2; //文字在画布中的x坐标
        int y = mHeight / 2 + mTxtHeight / 4; //文字在画布中的y坐标
        canvas.drawText(text, x, y, mPaintText);
    }

调用canvas的drawText方法绘制文字,x,y是文字中心位置的坐标。测量文字宽高有两种方式,这里用的是更精确的getFontMetrics方法。

画路径

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.reset();
                path.moveTo(event.getX(), event.getY());//原点移动至手指的触摸点
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(event.getX(), event.getY());
                break;
        }
        mCanvas.drawPath(path, pathPaint);
        invalidate();
        return true;
    }

最终效果图

完整代码

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
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;
 
import androidx.annotation.Nullable;
 
import com.example.androidprogressbar.R;
 
public class ScratchCard extends View {
 
    private Bitmap mBitmapBackground;
    private Bitmap mBitmapFront;
    private Canvas mCanvas;
    private Paint pathPaint;
    private Path path;
    private Paint mPaintText;
 
    public ScratchCard(Context context) {
        super(context);
        init();
    }
 
    public ScratchCard(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
 
    public ScratchCard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    private void init() {
        pathPaint = new Paint();
        pathPaint.setAlpha(0);
        pathPaint.setStyle(Paint.Style.STROKE);
        pathPaint.setStrokeWidth(70);
        pathPaint.setXfermode(new PorterDuffXfermode((PorterDuff.Mode.DST_IN)));//混合模式
        pathPaint.setStrokeJoin(Paint.Join.ROUND);//线段之间连接处的样式
        pathPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔的线冒样式
        path = new Path();
        mBitmapBackground = BitmapFactory.decodeResource(getResources(), R.drawable.card);
        mCanvas = new Canvas();
        mPaintText = new Paint();
        mPaintText.setColor(Color.WHITE);
        mPaintText.setTextSize(100);
        mPaintText.setStrokeWidth(20);
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmapBackground, 0, 0, null);
        canvas.drawBitmap(mBitmapFront, 0, 0, null);
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmapBackground = getBitmap(mBitmapBackground, w, h);
        mBitmapFront = Bitmap.createBitmap(mBitmapBackground.getWidth(),
                mBitmapBackground.getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas.setBitmap(mBitmapFront);
        drawText(mCanvas, w, h);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.reset();
                path.moveTo(event.getX(), event.getY());//原点移动至手指的触摸点
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(event.getX(), event.getY());
                break;
        }
        mCanvas.drawPath(path, pathPaint);
        invalidate();
        return true;
    }
 
    private void drawText(Canvas canvas, int mWidth, int mHeight) {
        String text = "赶紧刮开吧";
        canvas.drawColor(Color.GRAY);
        Paint.FontMetrics fm = mPaintText.getFontMetrics();
        int mTxtWidth = (int) mPaintText.measureText(text, 0, text.length());
        int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
        int x = mWidth / 2 - mTxtWidth / 2; //文字在画布中的x坐标
        int y = mHeight / 2 + mTxtHeight / 4; //文字在画布中的y坐标
        canvas.drawText(text, x, y, mPaintText);
    }
 
    public Bitmap getBitmap(Bitmap bm, int newWidth, int newHeight) {
        // 获得图片的宽高
        int width = bm.getWidth();
        int height = bm.getHeight();
        // 计算缩放比例
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 取得想要缩放的matrix参数
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        // 得到新的图片
        Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
        return newbm;
    }
 
}

以上就是Android通过自定义view实现刮刮乐效果详解的详细内容,更多关于Android刮刮乐的资料请关注脚本之家其它相关文章!

相关文章

  • Android数据持久化之ContentProvider机制详解

    Android数据持久化之ContentProvider机制详解

    这篇文章主要介绍了Android数据持久化之ContentProvider机制,结合实例形式分析了ContentProvider机制的原理与相关使用技巧,需要的朋友可以参考下
    2017-05-05
  • Android解析服务器端发来的xml数据示例

    Android解析服务器端发来的xml数据示例

    Android跟服务器交互数据,有时数据量大时,就需要以xml形式的交互数据,下面与大家分享下使用XmlPullParser来解析xml数据,感兴趣的朋友可以参考下哈
    2013-06-06
  • Android自定义下拉刷新控件RefreshableView

    Android自定义下拉刷新控件RefreshableView

    这篇文章主要介绍了Android自定义下拉刷新控件RefreshableView,支持所有控件的下拉刷新,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • loadavg数据异常引发问题起源分析

    loadavg数据异常引发问题起源分析

    这篇文章主要为大家介绍了loadavg数据异常引发问题起源分析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Android端使用Modbus协议的简单方法

    Android端使用Modbus协议的简单方法

    Modbus协议是全球第一个用于工业现场的总线协议,与外设交互可以采用串口通信,tcp等方式,这篇文章主要给大家介绍了关于Android端使用Modbus协议的简单方法,需要的朋友可以参考下
    2021-11-11
  • 使用Android WebSocket实现即时通讯功能

    使用Android WebSocket实现即时通讯功能

    即时通讯(Instant Messaging)最重要的毫无疑问就是即时,不能有明显的延迟,要实现IM的功能其实并不难,目前有很多第三方,比如极光的JMessage,都比较容易实现。本文通过实例代码给大家分享Android WebSocket实现即时通讯功能,一起看看吧
    2019-10-10
  • Android自定义ImageView实现圆角功能

    Android自定义ImageView实现圆角功能

    这篇文章主要为大家详细介绍了Android自定义ImageView实现圆角功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • Jetpack Compose自定义动画与Animatable详解

    Jetpack Compose自定义动画与Animatable详解

    在今年的Google/IO大会上,亮相了一个全新的 Android 原生 UI 开发框架-Jetpack Compose, 与苹果的SwiftIUI一样,Jetpack Compose是一个声明式的UI框架,这篇文章主要介绍了Jetpack Compose自定义动画与Animatable
    2022-10-10
  • Android实现短信验证码自动填写功能

    Android实现短信验证码自动填写功能

    这篇文章主要介绍了Android实现短信验证码自动填写功能,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • flutter showModalBottomSheet常用属性及说明

    flutter showModalBottomSheet常用属性及说明

    这篇文章主要介绍了flutter showModalBottomSheet常用属性及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09

最新评论