需求和问题
需求是在一个自定义view中放置两张重叠的bitmap,然后局部擦除顶部的那一张图,以此可以看到底部的图片。
大致思路:
//底部的图片已经通过其他方式绘制完成
canvas.drawBitmap(topBitmap, 0f, 0f, null)
// 画笔的建立需要在onDraw外面执行,这里为了叙述方便才放在这里
val clearPaint = Paint()
clearPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.XOR)
canvas.drawCircle(x, y, r, clearPaint)
但是并没有出现预期的效果,在drawCircle的地方,显示的背景为黑色,并不能看到底部的背景图。
网上的说法
1. 图片的格式问题,JPEG不支持透明。
但是这里并没有用到图片本身的透明属性,而是将整个canvas或是bitmap“设置为透明”。另外,按照这种说法,所有设置透明度时只能使用png格式的图片,不符合现实状况,也不利于用户使用。
2. Bitmap.Config
顺便复习一下
Possible bitmap configurations. A bitmap configuration describes how pixels are stored. This affects the quality (color depth) as well as the ability to display transparent/translucent colors.
这个属性代表了Bitmap可以的配置情况。一个配置描述的是这些像素信息是如何存储的。这个影响到了图片质量和透明度。
ALPHA_8:Each pixel is stored as a single translucency (alpha) channel.
每个像素信息只存储了alpha这一项信息。
ARGB_4444:This field was deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888
instead.
这个值在level 13的时候就已经不被建议使用了,因为这是一个低质量的配置,建议使用ARGB_8888
ARGB_8888:Each pixel is stored on 4 bytes. This configuration is very flexible and offers the best quality. It should be used whenever possible.
每个像素信息占用4个字节(即32个二进制位)的存储空间,alpha、red、green、blue各占8个二进制位。这个配置项是非常灵活,并提供了最好的质量。他应该在可能的情况下尽量被使用。
RGB_565:Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with 5 bits of precision (32 possible values), green is stored with 6 bits of precision (64 possible values) and blue is stored with 5 bits of precision.This configuration may be useful when using opaque bitmaps that do not require high color fidelity
每个像素信息占用2个字节(即16位二进制位)并且至存储了RGB的信息没有alpha信息,其中Red5位,Green6位,Blue5位。这个配置项在不需要提供透明度的情况下更有用。
这个说法不恰当的理由,跟png格式如出一辙。
3. 硬件加速的问题
在构造函数中(Kotlin选手在init方法中)关闭硬件加速:
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
问题解决!
探究原因
有关硬件加速
从Android 3.0(API Level 11)开始,Android支持硬件加速,从Android 4.0开始,手机支持。硬件加速可充分利用GPU的特性,使得界面渲染更加平滑,但是会消耗更多内存RAM。硬件加速自身并非完美,在某些Android 5的rom上,由于内存RAM分配的问题,如果代码不当,会引发闪屏、花屏等渲染问题。
1.Android3.0(API level 11)开始,2D渲染管道支持硬件加速,也就是说绘制操作可以使用GPU绘制在View的canvas上。使用硬件加速需要更多的资源,所以app会消耗更多内存。
2.硬件加速在Target API >= 14时是默认开启的。
3.硬件加速还不支持所有的2D绘图命令,开启后可能会影响自定义View和绘图操作。异常通常是不可见元素、运行异常、或者错误的像素点。
4.如果只使用系统的View和Drawable,则没有任何副作用。
5.如果app里有自定义的绘图操作,需要在开启硬件加速的设备上测试来发现问题。
控制硬件加速
可以在4个级别上控制硬件加速
Application:(AndroidManifest.xml)
<application
...
android:hardwareAccelerated="true"
...
>
Activity:(AndroidManifest.xml)
<activity
android:name=".MainActivity"
android:hardwareAccelerated="true">
Window
window.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
View
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
在这四个级别上,Application和Activity是可以自由控制开关的,Window只能控制打开,View只能控制关闭。(在自己的例子中就是关闭了view的硬件加速)
我的问题通过这个小技巧解决了,还没有深入学习原理,但希望可以帮到他人。
网友评论