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

效果图:

实现原理:

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

关键代码:

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

创建bitmap:

    @Overrideprotected 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方法。

画路径:

    @Overridepublic 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);}@Overrideprotected void onDraw(Canvas canvas) {canvas.drawBitmap(mBitmapBackground, 0, 0, null);canvas.drawBitmap(mBitmapFront, 0, 0, null);}@Overrideprotected 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);}@Overridepublic 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实现刮刮乐相关推荐

  1. Android自定义View进阶-MotionEvent详解

    欢迎Follow我的GitHub, 关注我的CSDN. 其余参考Android目录 我们微信公众号:杨守乐 推荐文章: 如果你喜欢上了一个程序员小伙,献给所有的程序员女友 学习资料(干货汇集)不断更新 ...

  2. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  3. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  4. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  5. Android自定义View:ViewGroup(三)

    自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...

  6. android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

  7. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  8. android中仿qq最新版抽屉,Android 自定义View实现抽屉效果

    Android 自定义View实现抽屉效果 说明 这个自定义View,没有处理好多点触摸问题 View跟着手指移动,没有采用传统的scrollBy方法,而是通过不停地重新布局子View的方式,来使得子 ...

  9. Android 自定义 圆环,Android自定义view实现圆环效果实例代码

    先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...

  10. android自定义抽奖,Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...

最新文章

  1. NAND FLASH
  2. 为什么我的电脑上安装不上python-python安装不上
  3. 详解数据库引擎与SQL语句增删改查(非常详细,带例)
  4. 《漫画算法》源码整理-5 排序算法
  5. 使软件可二次开发_基于C++的?UG二次开发
  6. 记“debug alipay”一事
  7. Source Insight 4.0安装教程(PS:附安装包及卸载重新安装等注意事项)
  8. 2010/9/12学习历程
  9. 对工作生活的一点感悟
  10. 3-18pytorch与矩阵分解PCA
  11. c# 日期转换为单词
  12. vue中 点击二维码刷新图片(src不变)
  13. SQL 数据库 学习 012 数据库关系图
  14. raid5换硬盘显示ready_服务器RAID磁盘坏道修复实战
  15. 开源框架Banner实现图片轮播
  16. 怎么调整图片dpi大小?如何修改分辨率?
  17. NodeMcu arduino ESP8266 使用Ticker库(多任务处理)
  18. android 地球坐标 火星坐标系,IOS开发入门之iOS开发火星坐标系与地球坐标系解析...
  19. IOS目标检测(翻译)
  20. CD7388CZ功放IC,4x41W汽车音响功率放大电路,车机标配IC

热门文章

  1. 计算机之间的连接方式
  2. Fast Perceptual Image Enhancement
  3. GDI对象与核心对象
  4. 表数据操作(插入数据、修改数据、删除数据)
  5. 牛客练习赛20 托米历险记
  6. IIS服务器支持flv,f4v,mp4在线播放
  7. 计算机网络的定义和组成元素
  8. 计算机网络参考模型(OSI讲解)
  9. ldap over ssl php,ldapsearch over ssl/tls不起作用
  10. Hive总结 --hive表的创建,删除和修改