Android通过自定义view实现刮刮乐效果详解
作者:吐尔洪江Coding 发布时间:2022-02-15 02:26:09
标签:Android,刮刮乐
前言
已经有两个月没有更新博客了,其实这篇文章我早在两个月前就写好了,一直保存在草稿箱里没有发布出来。原因是有一些原理性的东西还没了解清楚,最近抽时间研究了一下混合模式,终于也理解了刮刮乐是怎么实现的,所以想继续分享一下自己的一些心得,先上效果图。
效果图:
实现原理
其实刮刮乐实现原理也不算很复杂,最关键的还是需要了解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;
}
}
来源:https://blog.csdn.net/daydayup05/article/details/123784528


猜你喜欢
- 项目背景在做项目的时候,把SpringBoot的项目打包成安装包了,在客户上面安装运行,一切都是那么的完美,可是发生了意外,对方突然说导出导
- 线程池类图我们最常使用的Executors实现创建线程池使用线程主要是用上述类图中提供的类。在上边的类图中,包含了一个Executor框架,
- 效果图代码 package com.jh.timelinedemo;import android.content.Context;
- 本项目使用的环境:开发工具:Intellij IDEA 2017.1.3springboot: 1.5.6jdk:1.8.0_161mave
- 本文实例讲述了C#判断页面中的多个文本框输入值是否有重复的实现方法,分享给大家供大家参考。具体实现方法如下:List<string&g
- Android系统提供了MediaScanner,MediaProvider,MediaStore等接口,并且提供了一套数据库表格,通过Co
- public static boolean isMobileNumber(String mobiles) {return Pattern.c
- 什么是JMMJMM全称Java Memory Model, 中文翻译Java内存模型,一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问
- 简评:作为一位 Android 开发者,Android Studio 肯定是每天都要打交道的,熟练掌握其中的快捷键等技巧可以提高我们不少的效
- 本文实例为大家分享了Android实现指针刻度转盘的具体代码,供大家参考,具体内容如下一. 先上个效果图,实现如图所示刻度转盘和2个文本的绘
- 前言:上午写代码时还好好的,下午不知道怎么回事突然就不显示logcat日志了,觉得很奇怪,于是开始找各种解决办法!现象如图所示,logcat
- 一、TPHTable Per Hierarchy (默认,每个层次一个表)每个层次结构共用一个表,类的每一个属性都必须是可空的。1、默认行为
- 通常在使用service更新应用时最常出现的问题就是Notification进度的更新问题、service在什么时间关闭以及需要我们自己在S
- primary_text_yellow.xml <?xml version="1.0" encoding=&quo
- 本文实例讲述了C#读取系统字体颜色与大小的方法。分享给大家供大家参考。具体分析如下:首先,说到字体、颜色,我们应该想到System.Draw
- notification是一种让你的应用程序在没有开启情况下或在后台运行警示用户。它是看不见的程序组件(Broadcast Receiver
- 这里给一个样例树:代码:#include <stdio.h> #include <string.h>#include
- 演示1 - 代理创建器public class A17 { public static void main(Str
- 将字母全部转换为大写或小写,在C#编程中是一个非常常见的功能。在开发过程中,经常需要验证用户登录,用户在输入用户名时可能不区分大小写,如果我
- 相信大家在系统学习jvm的时候都会有遇到过这样的问题,散落的jvm知识点知道很多,但是真正在线上环境遇到一些莫名其妙的gc异常时候却无从下手