刮奖效果控件--ScratchView

时间:2022-06-14 13:07:49

    最近看了一篇公众号推送的文章,关于Android刮奖效果的自定义控件,感觉蛮有意思,所以就模仿他也写了一个自定义ScratchView,人家的文笔不错,所以建议可以先看他的文章:ScratchView:一步步打造万能的 Android 刮奖效果控件;然后再来阅读我的,那为什么还要看我的呢?因为我针对内存优化和监听时机两个方面做了改进。

    考虑如果你没看刚才那位的文章,那后面说的优化可能就云里雾里,所以还是说一下ScratchView的实现细节吧。ScratchView的实现思路就是通过绘制和被覆盖view一样大小但是不同透明度的蒙层,重写touch事件的响应来修改蒙层的透明度,实现刮奖的效果。

1.蒙层效果。

    蒙层的本质是一个bitmap,通过不同颜色的画笔Paint,在onDraw的时候把bitmap绘制到画布Canvas上。下面这段是mask画笔的初始化,设置抗锯齿和防抖,最后是设置mask的颜色。

 刮奖效果控件--ScratchView

    在view的宽和高确定之后,创建一个等大的bitmap,然后把这个bitmap绘制到画布上。

 刮奖效果控件--ScratchView

    因为是自定义view,所以考虑可以在xml中设置这个view的一些属性,比如这里的蒙层的颜色属性。这里可以自定义它的一些属性,在attr.xml中增加这个ScratchView的属性,每条attr都是一个自定义属性,如下图。

 刮奖效果控件--ScratchView

    在java代码中获取这些设置的属性,需要在构造函数中通过TypedArray来获取。需要考虑和属性的值的类型一一对应。

刮奖效果控件--ScratchView 

    蒙层如果只是一种颜色,会比较单调,因此会考虑增加一些图案,比如logo在蒙层上面,这也称为水印效果。要实现水印效果,可以使用BitmapDrawable,利用它的TileMode来实现不同的水印效果。比如镜面对称的MIRROR,简单重复的REPEAT等。

 刮奖效果控件--ScratchView

    现在我们展示一下,有蒙层的效果图。

 刮奖效果控件--ScratchView

2.刮奖效果。

    刮奖效果本质是监听手指的touch事件,根据move事件,把经过的像素点的透明度设置为0,从而达到被擦除的效果。

    新建一个擦除画笔,设置画笔的Xfermode模式,里面有一种PorterDuff.Mode.CLEAR模式,在很多画板程序里实现橡皮擦效果,这里也借鉴。

 刮奖效果控件--ScratchView

   对touch事件的监听,只需要处理DOWNMOVEUP事件即可。

 刮奖效果控件--ScratchView

    对于DOWN事件,需要重置擦除的路径,然后记录新的起点。对于MOVE事件,需要记录移动的路径,当然这里有最小的移动距离,然后利用擦除画笔,把路径上的点透明度修改掉,达到擦除效果。对于UP事件,需要重置擦除路径。

 刮奖效果控件--ScratchView

    现在我们展示下擦除效果。

 刮奖效果控件--ScratchView

3.设置监听。

    考虑到刮奖,当大部分的图案显示出来的时候,用户就不会继续刮下去,这时候,程序也应该有一个处理,提示中奖或者其他动作。因此,这里设置用户刮奖的时候,显示擦除部分的占比,当占比超过一个默认值时,就认为刮奖完成,需要给外面一个回调的接口。

 刮奖效果控件--ScratchView

    已擦除部分和整个view的占比计算是一个比较重要的点。因为mask是用bitmap实现的,而bitmap是可以获取到每个像素点的透明度,所以可以通过一个数组存这个bitmap的每个像素点的透明度,每次擦除的时候都遍历一次,然后计算占比,就可以获得已擦除部分的占比。

 刮奖效果控件--ScratchView

    mPixels就是mask的所有像素点透明度保存的数组,调用bitmapgetPixels可以获取当前bitmap的透明度并保存到这个int数组里面。通过计算已擦除数量mErasePixelCount和总的数量mTotalErasePixel的比例,得到实际占比。并把这个比例回调给监听者。

    另外,这里是在touch事件的UP事件时候才处理是否擦除已完成的回调,而且通过设置mIsCompleted这个布尔值,保证回调只触发一次。

   这个的效果需要配合其他观察者一起使用,就不展示了。

4.重置和清除效果。

    重置的实现,本质是通过把maskbitmap重新绘制一遍;同理清除效果本质是把maskbitmap的所有像素点透明度置位0

 刮奖效果控件--ScratchView

5.内存优化。

    最初运行程序,每次重置都会发生内存稳定增加。如下图:

 刮奖效果控件--ScratchView

    分析发现,因为重置是会调用createMaskBitmap函数,而这个函数内部重新new bitmap,同时还会把存bitmap像素点透明度的数组也会重新new一遍。

    这里直接给出内存优化后的代码。

 刮奖效果控件--ScratchView

    这里主要考虑一下两点:第一点,bitmap能不能不new,答案是肯定的,但是需要判断,如果viewsize改变了,或者bitmap被回收了,就需要重新new,但是如果没有的话,我们直接复用之前的bitmap,不要再new。第二点,bitmap的像素点透明度的数组可不可以复用,答案也是肯定的,也只有在viewsize发生变化的时候才需要去new。所以优化后的代码会增加这两个判断。

    优化后的内存如下图所示,只有在第一次new bitmap的时候发生内存增加,后面操作基本没有发生内存比较大幅度的变化。

 刮奖效果控件--ScratchView

     自定义的ScratchView的代码地址:刮奖效果控件--ScratchView