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

ScratchView的实现细节吧。ScratchView的实现思路就是通过绘制和被覆盖view一样大小但是不同透明度的蒙层,重写touch事件的响应来修改蒙层的透明度,实现刮奖的效果。

1.蒙层效果。

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

 

scratch的容器化 helloworld scratch控件_ScratchView

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

 

scratch的容器化 helloworld scratch控件_ScratchView_02

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

 

scratch的容器化 helloworld scratch控件_ScratchView_03

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

scratch的容器化 helloworld scratch控件_ScratchView_04

 

logo在蒙层上面,这也称为水印效果。要实现水印效果,可以使用BitmapDrawable,利用它的TileMode来实现不同的水印效果。比如镜面对称的MIRROR,简单重复的REPEAT等。

 

scratch的容器化 helloworld scratch控件_android_05

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

 

scratch的容器化 helloworld scratch控件_重置_06

2.刮奖效果。

touch事件,根据move事件,把经过的像素点的透明度设置为0,从而达到被擦除的效果。

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

 

scratch的容器化 helloworld scratch控件_ScratchView_07

touch事件的监听,只需要处理DOWN,MOVE和UP事件即可。

 

scratch的容器化 helloworld scratch控件_自定义控件_08

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

 

scratch的容器化 helloworld scratch控件_android_09

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

 

scratch的容器化 helloworld scratch控件_android_10

3.设置监听。

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

 

scratch的容器化 helloworld scratch控件_重置_11

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

 

scratch的容器化 helloworld scratch控件_ScratchView_12

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

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

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

4.重置和清除效果。

mask的bitmap重新绘制一遍;同理清除效果本质是把mask的bitmap的所有像素点透明度置位0。

 

scratch的容器化 helloworld scratch控件_android_13

5.内存优化。

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

 

scratch的容器化 helloworld scratch控件_android_14

createMaskBitmap函数,而这个函数内部重新new bitmap,同时还会把存bitmap像素点透明度的数组也会重新new一遍。

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

 

scratch的容器化 helloworld scratch控件_像素点_15

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

new bitmap的时候发生内存增加,后面操作基本没有发生内存比较大幅度的变化。

 

scratch的容器化 helloworld scratch控件_ScratchView_16

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