bitmap和drawable的互相转换

零、概念

  • Bitmap (位图) :它是极其具体的像素数据。它就像一张冲洗出来的实体照片,实实在在地占据着内存(宽 × 高 × 每个像素的字节数)。
  • Drawable (可绘制对象) :它是高度抽象的"绘画指令" 。它代表"任何可以被画到 Canvas 上的东西"。它可以是一张 Bitmap(BitmapDrawable),也可以是一堆适量路径(VectorDrawable),还可以是一个纯色色块(ColorDrawable)或者一个带圆角的矩形(ShapeDrawable)。

一、 Bitmap 转换为 Drawable (具体 -> 抽象)

这个过程非常简单。既然 Drawable 是一个宽泛的概念,我们只需要找一个专门负责包装 BitmapDrawable 子类------BitmapDrawable,把它包起来就可以了。

代码实现(Kotlin):

Kotlin 复制代码
// 假设你已经有了一个 bitmap 对象
val bitmap: Bitmap = ... 

// 转换:使用 Resources 主要是为了让 Drawable 知道当前的屏幕密度(dpi),从而正确缩放
val drawable: Drawable = BitmapDrawable(context.resources, bitmap)

底层逻辑:

这只是包了一层壳,BitmapDrawabledraw(Canvas) 方法底层做的就是调用 Canvas.drawBitmap()


二、 Drawable 转换为 Bitmap (抽象 -> 具体)

这个过程就相对复杂了。因为你要把一个"抽象的绘画指令"强制变成"实实在在的像素矩阵"。

我们分为两种情况来处理:

1. 它是 BitmapDrawable 时的捷径

如果这个 Drawable 的真身本来就是一张位图(比如你从 ImageView 里拿出来的 jpg/png 图片),我们直接把外壳剥掉就行了。

Kotlin 复制代码
if (drawable is BitmapDrawable) {
    val bitmap: Bitmap? = drawable.bitmap
    if (bitmap != null) {
         // 直接拿到,完事!
         return bitmap
    }
}

2. 通用转换:万物皆可 Canvas(强转像素)

如果它是一个矢量图(VectorDrawable)或者一个纯色(ColorDrawable),它本身是不包含像素矩阵的。

这时候,我们需要准备一张"空白的画布和画纸",让这个 Drawable 自己在上面画一遍,画完之后,这张"纸"就是我们要的 Bitmap

传统的手写代码实现:

Kotlin 复制代码
fun drawableToBitmap(drawable: Drawable): Bitmap {
    // 1. 如果是 BitmapDrawable,走捷径
    if (drawable is BitmapDrawable && drawable.bitmap != null) {
        return drawable.bitmap
    }

    // 2. 处理宽高。有些 Drawable(比如纯色)是没有内在宽高的,必须给个保底值(比如 1x1)
    val width = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else 1
    val height = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else 1

    // 3. 准备"画纸"(创建一张空白的 Bitmap)
    // ARGB_8888 意味着支持透明通道,质量最好
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)

    // 4. 准备"画板"(将 Bitmap 绑定到 Canvas 上)
    val canvas = Canvas(bitmap)

    // 5. 规定绘制范围(极其重要!不设置 bounds Drawable 默认画在 0,0,0,0 上,什么都看不到)
    drawable.setBounds(0, 0, canvas.width, canvas.height)

    // 6. 让 Drawable 开始在我们的画板上作画
    drawable.draw(canvas)

    // 7. 画作完成,返回画纸
    return bitmap
}

现代 Android 开发的"作弊代码" (推荐用法)

如果你正在使用 Kotlin,并且项目中引入了 androidx.core:core-ktx 库(现代 Android 项目几乎默认都会引入),那么 AndroidX 官方已经利用扩展函数帮你把上述极其繁琐的代码全部封装好了。

你只需要一行代码:

Kotlin 复制代码
import androidx.core.graphics.drawable.toBitmap

// 将 Drawable 转换为 Bitmap,底层逻辑就是上面讲的 Canvas 绘制逻辑
val bitmap = drawable.toBitmap()

// 你甚至可以顺便指定转换后的大小和配置
val scaledBitmap = drawable.toBitmap(width = 100, height = 100, config = Bitmap.Config.ARGB_8888)

总结:

日常开发中,直接使用 AndroidX 提供的 .toBitmap() 扩展函数是最高效、最不易出错的方式。但理解"准备空白 Bitmap -> 绑定 Canvas -> 调用 draw"这套底层第一性原理,对于你未来去实现自定义 View、做离屏渲染(我们之前聊过的 saveLayer 也是类似的开辟空白画板的思想)都是极其有帮助的。

相关推荐
美狐美颜SDK开放平台2 小时前
美颜SDK接入流程详解:Android、iOS、鸿蒙兼容方案解析
android·人工智能·ios·华为·harmonyos·美颜sdk·视频美颜sdk
笔夏3 小时前
【安卓学习之FloatingActionButton】按钮太小
android·学习
XD7429716364 小时前
科技早报晚报|2026年5月15日:无摄像头空间感知、Android 设备实验室与视频检索代理,今天更值得跟进的 3 个技术机会
android·科技·音视频·开源项目·边缘ai·开发者工具
应用市场4 小时前
Android Verified Boot 2.0 安全启动原理详解
android·安全
只可远观4 小时前
Android XML命令式和Jetpack Compose声明式UI
android·xml
他是龙5514 小时前
DVWA 靶场深度解析:文件包含 & 文件上传(Low → Impossible)
android
_李小白4 小时前
【Android车载学习笔记】第一天:Android Automotive OS介绍
android·笔记
aqi004 小时前
FFmpeg开发笔记(一百零一)跨平台的开源音视频移动框架MobileFFmpeg
android·ffmpeg·音视频·直播·流媒体