0%

在TextView里面加载动态Drawable

参照sunhapper的思路,设置Drawable.Callback来刷新TextView,但实际操作中踩了一个大坑:应用是用的Glide来加载图片,奇怪的是即使invalidate掉整个TextView也没法刷新Glide自带的GifDrawable,相对的android-gif-drawable即使没有再次调用Drawable.draw也能很好的刷新。

由于android-gif-drawable是用OpenGL来刷新GIF图片,一开始没敢深究,就把GifDrawable转成android-gif-drawable。后来,打算用CircularProgressDrawable来显示加载进度,这时候Drawable就没法转成android-gif-drawable了。

被迫研究了android-gif-drawable的代码,发现它是创建了一个Bitmap来给OpenGL进行绘制,再在Drawable.onDraw里把Bitmap绘制出来。相对的,GifDrawable是在Drawable.draw里直接绘制,由于Drawable.draw只调用一次,就没能显示动态的图片。

也就是说,在TextView开启硬件加速的情况下,虽然Drawable.draw只被调用一次,但是Bitmap会以引用的形式传递给GPU,修改Bitmap就能在下次绘制时更新图像

那么我们继承AnimationDrawable来写个Wrapper,它包含一个drawable变量,当然也有一个Bitmap缓存:

1
2
3
var drawable: Drawable? = null

private var mBuffer: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)

首先,在每次修改drawable变量时,把原drawable停掉,把Bitmap的大小设置成和drawable一样大,并加上Drawable.Callback

1
2
3
4
5
6
7
8
(this.drawable as? Animatable)?.stop()
this.drawable?.callback = null
this.drawable = drawable
this.drawable?.callback = drawableCallback
(drawable as? Animatable)?.start()
setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
mBuffer = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888)
updateBuffer()

drawableCallback负责在drawable更新的时候刷新Bitmap,并调用容器的invalidate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun updateBuffer() {
val bufferCanvas = Canvas(mBuffer)
bufferCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawable?.draw(bufferCanvas)
invalidateSelf()
}

val drawableCallback = object : Callback {
override fun invalidateDrawable(who: Drawable) {
updateBuffer()
container.get()?.invalidate()
}

override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
container.get()?.postDelayed(what, `when`)
}

override fun unscheduleDrawable(who: Drawable, what: Runnable) {
container.get()?.removeCallbacks(what)
}
}

最后在draw里把Bitmap绘制出来就完成了:

1
2
3
4
5
private val mPaint = Paint(Paint.FILTER_BITMAP_FLAG or Paint.DITHER_FLAG)

override fun draw(canvas: Canvas) {
canvas.drawBitmap(mBuffer, bounds, bounds, mPaint)
}

完整代码传送门