文章

Android Bitmap 与 Drawable

做了好几年 Android 开发,很惭愧地说,其实我也不太能分清这两个的区别与使用场景,直到今天。

本质与区别

Bitmap 本质上是一个位图数据,它只是单纯地存储一张图片,仅此而已。

Drawable 这是一个上层的绘制工具,有点类似 View,可以调用 Canvas 进行绘制。但 View 还承担布局、测量等工作,Drawable 就轻量多了,只负责绘制。Drawable 默认没有颜色信息,甚至连大小都没有,所以记得先设置大小后再进行绘制,然后才有了像素数据。

比如:

canvas.drawBitmap(bitmap, 0f, 0f, null)
val drawable = ColorDrawable().apply { setBounds(0, 0, 200, 200) }
drawable.draw(canvas)

这个小例子体现了 Bitmap 和 Drawable 的层次/功能差异。

因为 Bitmap 与 Drawable 根本不是一个层面的东西,所以两者不能直接互转。所谓的“互转”其实是一种“生产”:

  • Bitmap -> Drawable: 创建一个 Drawable,内部保存 Bitmap 的实例,在 draw() 方法中利用 Canvas 把图片绘制出来。
  • Drawable -> Bitmap: 创建一个 Bitmap,把 Drawable 内的图形画上去。

这就类似我们能找一个可以临摹某幅画的画家,也可以找一个画家画个画买走,但不能说「画家」和「画」可以互转。

Android ktx 库已经提供了 Drawable.toBitmap()Bitmap.toDrawable() 便捷函数,具体实现就是上面说的思路。

自定义 Drawable

有人说 「自定义 Drawable 没有意义,因为系统已经提供了足够多类型的 Drawable」。

其实系统也提供了许多 View,但总有一些需求是办不到的,所以不能说它没有意义。除此之外,自定义 Drawable 还可以重用。 比如不同的 View 需要显示同一个比较复杂的东西,就可以抽出来作为 Drawable。

自定义 Drawable 与自定义 View 类似,但更简单,大概结构如下:

class MeshDrawable : Drawable() {
  override fun draw(canvas: Canvas) {}
  override fun setAlpha(alpha: Int) {}
  override fun getAlpha(): Int {}
  override fun getOpacity(): Int {}
  override fun setColorFilter(colorFilter: ColorFilter?) {}
  override fun getColorFilter(): ColorFilter? {}
}

其中大部分方法都可以和 Paint 关联起来,除了 getOpacity()。这个方法与 getAlpha() 不同,不用返回具体的透明度,而是指明这个 Drawable 是所有区域都不透明,还是完全透明,还是只有部分区域透明,用于与底部/前部其他图形进行颜色混合。