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刮刮乐的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android使用Canvas对象实现刮刮乐效果

    在淘宝.京东等电商举办活动的时候,经常可以看到在移动客户端推出的各种刮奖活动,而这种活动也受到了很多人的喜爱.从客户端的体验来说,这种效果应该是通过网页来实现的,那么,我们使用Android的自带控件能不能实现这种刮刮乐的效果呢?当然可以,本篇文章将介绍使用Canvas这个对象,如何实现"刮刮乐"的效果. 先看效果图 下面我们看一下如何使用代码实现 布局文件 <FrameLayout xmlns:android="http://schemas.android.com/a

  • 基于Android自定义控件实现刮刮乐效果

    只是简单的实现了效果,界面没怎么做优化,不过那都是次要的啦!!相信大家都迫不及待的想看效果图了吧, 其中主要的彩票视图类和橡皮擦类都是通过代码的方式构建视图,布局文件就一个主activity_main 上代码!! 主activity: package com.guaguale; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.View; imp

  • Android实现刮刮乐示例分析

    微信公众号有很多都做刮刮乐的活动,本文就实现了刮刮乐的效果,具体代码如下: 首先要做一个类似橡皮擦的东西吧,然后才能把纸上的笔迹擦除 /** * FileName: SplashActivity.java * * @desc 橡皮擦功能,类似刮刮乐效果 * @author HTP * @Date 20140311 * @version 1.00 */ public class Text_Rubbler extends TextView { private float TOUCH_TOLERANC

  • Android刮刮乐效果-proterDuffXfermode的示例代码

    先看看实现的效果 这个场景主要是模拟我们有些app里面的刮刮乐中奖的效果,主要是利用Android的proterDuffXfermode这个类去实现的. proterDuffXfermode 在用Android中的Canvas进行绘图时,可以通过使用PorterDuffXfermode将所绘制的图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素颜色值,这样会创建很多有趣的效果.PorterDuffXfermode的功能十分的强大,其他的

  • Android studio实现刮刮乐的方法

    本文实例为大家分享了Android studio实现刮刮乐的具体代码,供大家参考,具体内容如下 MainActivity public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mai

  • Android自定义View实现纵向跑马灯效果详解

    首先看看效果图(录制的gif有点卡,真实的效果还是很流畅的) 实现思路 通过上面的gif图可以得出结论,其实它就是同时绘制两条文本信息,然后通过动画不断的改变两条文本信息距离顶部的高度,以此来实现滚动的效果. 具体实现 首先定义一些要用到的属性 <declare-styleable name="MarqueeViewStyle"> <attr name="textSize" format="dimension" /> &l

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

    前言 已经有两个月没有更新博客了,其实这篇文章我早在两个月前就写好了,一直保存在草稿箱里没有发布出来.原因是有一些原理性的东西还没了解清楚,最近抽时间研究了一下混合模式,终于也理解了刮刮乐是怎么实现的,所以想继续分享一下自己的一些心得,先上效果图. 效果图: 实现原理 其实刮刮乐实现原理也不算很复杂,最关键的还是需要了解Paint的混合模式.因为刮刮乐是由两个bitmap组成的,一个是源图另一个是目标图,我们需要把目标图的颜色改成灰色,在源图上面盖上了一张灰色的目标图.当手指滑动屏幕时paint

  • Android自定义View中attrs.xml的实例详解

    Android自定义View中attrs.xml的实例详解 我们在自定义View的时候通常需要先完成attrs.xml文件 在values中定义一个attrs.xml 然后添加相关属性 这一篇先详细介绍一下attrs.xml的属性. <?xml version="1.0" encoding="utf-8"?> <resources> //自定义属性名,定义公共属性 <attr name="titleText" for

  • Android自定义view实现滚动选择控件详解

    目录 前言 需求 编写代码 主要问题 前言 上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec.onMeasure以及onLayout,接下来就用一个滚动选择的控件(View)来学一下onDraw的使用,并且了解下在XML自定义控件参数. 需求 这里就是一个滚动选择文字的控件,还是挺常见的,之前用别人的,现在选择手撕一个,核心思想如下: 1.有三层不同大小及透明度的选项,选中项放在中间 2.接受一个列表的数据,静态时显示三个值,滚动时显示四个

  • Android OpenGL仿自如APP裸眼3D效果详解

    目录 原理简介 & OpenGL 的优势 具体实现 1. 绘制静态图片 2. 让图片动起来 3. 几个反直觉的细节 4. 帕金森综合征? 源码 原理简介 & OpenGL 的优势 裸眼 3D 效果的本质是——将整个图片结构分为 3 层:上层.中层.以及底层.在手机左右上下旋转时,上层和底层的图片呈相反的方向进行移动,中层则不动,在视觉上给人一种 3D 的感觉: 也就是说效果是由以下三张图构成的: 接下来,如何感应手机的旋转状态,并将三层图片进行对应的移动呢?当然是使用设备自身提供各种各样优

  • Android使用自定义View实现横行时间轴效果

    前言 本篇文章会说下如何使用并且要用麻烦的自定义 view 去实现时间轴效果,以及如何分析.实现自定义 view. 需要具备的知识:Paint.Canvas.自定义 view 的绘制流程. 欢迎留言交流. 一.已经有很多 RecycleView 实现时间轴的例子,为何还要费劲的使用自定义 view 去实现时间轴? 首先看下最终想要的效果: 根据上图可以总结出以下几点: 每个阶段要显示时间.阶段名.状态图标.中间有虚线: 文字上下交错显示: 相邻阶段的文字在垂直方向上是可以相交的: 时间轴的个数不

  • Android自定义View实现绘制虚线的方法详解

    前言 说实话当第一次看到这个需求的时候,第一反应就是Canvas只有drawLine方法,并没有drawDashLine方法啊!这咋整啊,难道要我自己做个遍历不断的drawLine?不到1秒,我就放弃这个想法了,因为太恶心了.方法肯定是有的,只不过我不知道而已. 绘制方法 最简单的方法是利用ShapeDrawable,比如说你想用虚线要隔开两个控件,就可以在这两个控件中加个View,然后给它个虚线背景. 嗯,理论上就是这样子的,实现上也很简单. <!-- drawable 文件 --> <

  • Android 自定义 View 中使用 Spannable的实例详解

    我们都知道 Android 中使用 Spannable 可以实现 TextView 富文本的显示,但是在自定义控件中如何使用 Spannable 绘制不同样式的文字呢? 例如这种效果,标题中的 分数字61 是粗体,分 是常规字体,并且相对于 61 更小些. 第一反应可能是使用 SpannableString.setSpan() 设置 RelativeSizeSpan, 然后在 onDraw() 中进行绘制,事实是这样实现是没有效果的,因为 onDraw() 中只能获取到 SpannableStr

  • Android实现自定义的卫星式菜单(弧形菜单)详解

    一.前言 Android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下: 1.动画的处理 2.自定义ViewGroup来实现卫星式菜单View (1)自定义属性 a. 在attrs.xml中定义属性 b. 在布局中使用自定义属性 c. 在自定义View中读取布局文件中的自定义属性 (2)onMeasure 测量 child 即测量主按钮以及菜单项 (3)onLayout 布局 child 即布局主按钮以及菜单项 (4)设置主按钮的选择动画 a.为菜单项menuItem添加平移动画和旋转动画 b

  • Android如何自定义EditText光标与下划线颜色详解

    前言 最近在写些小Demo复习基础,在用到EditText的时候突然发现之前几乎没有注意到它的光标和下划线的颜色,于是花了不少时间,看了不少博客,现在就来总结和分享一下收获,话不多说了,来一起看看详细的介绍: 1.第一印象:原生的EditText 我们要在原生的EditText上修改,首先当然要认识一下它的本来面目.在Android Studio中新建一个工程,让MainActivity继承于AppCompatActivity(为什么要这样做,后面再说),然后在MainActivity的布局中放

随机推荐