Bitmap 是什么
Bitmap 是位图信息的存储,即⼀个矩形图像每个像素的颜⾊信息的存储器。一个可能的bitmap的信息:
image:
width:640
height:400
pixel:
ffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOOffOOOO
Drawable 是什么
Drawable 是 Android 绘图系统中的“绘制工具”——本质上是一套“如何画”的规则或描述,而不是一张图片或者像素本身。
- Drawable 内部存储的不是像素数据,而是绘制规则。这些规则既可以简单到只是一种颜色,也可以复杂到支持动画、渐变、图层叠加等效果。
- 调用
Drawable.draw(Canvas)时,Drawable 会把内容绘制到指定的Canvas上。 - Drawable 本身不能独立显示,它必须被绘制到 Canvas(通常由 View 提供)上才会显示到屏幕。
- 在调用
draw()方法前,通常需要用setBounds()指定绘制区域,否则内容可能不会显示或显示不全。
动态绘制功能
-
Drawable 可以是静态的(比如只画一张图片),也可以是动态的,即根据状态或参数实时生成内容。
- 例如,
StateListDrawable能根据 View 的不同状态(按下、选中、失焦等)切换不同的 Drawable。 ShapeDrawable和GradientDrawable能在运行时生成不同形状、不同渐变色的图形,无需准备多个图片资源。
- 例如,
示例:创建一个渐变背景并应用到 View
val drawable = GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(Color.RED, Color.BLUE)
)
drawable.cornerRadius = 10f
imageView.background = drawable
Drawable 的多样性
Drawable 的类型非常丰富,不只限于图片:
- BitmapDrawable:直接包装 Bitmap 数据,显示静态图片。
- ShapeDrawable:绘制几何图形(如矩形、圆形等)。
- GradientDrawable:绘制渐变背景。
- VectorDrawable:支持矢量图形(缩放无损)。
- StateListDrawable:状态选择器,根据不同状态切换 Drawable。
- LayerDrawable:可叠加多个 Drawable,形成复杂效果。
- AnimationDrawable:支持帧动画,逐帧切换不同 Drawable。
这些类型为 Android UI 提供了强大的绘制和灵活性,开发者不必为每种状态准备一堆图片资源。
Drawable 的绘制机制
- Drawable 的核心职责是负责如何把内容绘制到 Canvas。
- Android 的 View 在
onDraw(Canvas)方法里,常常直接或间接调用 Drawable 的draw()方法来完成最终显示。 - 开发者可以自定义 Drawable,扩展 Drawable 类,重写 draw(),以实现各种特殊绘制逻辑。
自定义 Drawable 示例
class CustomDrawable : Drawable() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
override fun draw(canvas: Canvas) {
paint.color = Color.RED
canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), 50f, paint)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
}
性能与资源优化
使用 Drawable 的优势不仅在于灵活、动态,性能和资源利用也优于直接使用 Bitmap 图片:
- 资源复用:一套绘制规则可供多处使用(例如多个 View 共享同一个 Drawable 实例),降低内存消耗。
- 节省内存:矢量(如 VectorDrawable)、渐变(如 GradientDrawable)、图形(ShapeDrawable)等只需规则无需大图片,自动适配不同分辨率,节省大量内存。
- 分辨率独立:矢量、形状和渐变 Drawable 可自动适配各种屏幕密度,无需准备不同尺寸的图片资源。
- 灵活动态:可按需生成、修改、动画,而无需事先准备不同状态的图片。
小结
- Drawable 是一种绘制规则,不是图片数据本身,其绘制逻辑可以非常丰富。
- Drawable 类型多样,既可以表示图片,也可以表示动态生成的形状、渐变、动画等。
- 它们是 Android 绘制系统的基础,效率高,适合做背景、图标、动态效果和自定义绘制。
Bitmap 和 Drawable 的互相转换
Bitmap 表示像素数据,是图片的最底层数据结构(像素矩阵);Drawable 是绘制规则的封装,是 Android 图形系统用来“如何画”的对象。二者互补,但不是“直接互转”,而是可以从一个创建另一个。
1. Bitmap → Drawable
用 Bitmap 作为内容来构造 Drawable 的规则
-
用
BitmapDrawable包装:val drawable = BitmapDrawable(resources, bitmap) -
然后你可以把 drawable 设置给 ImageView、Canvas、或者任何需要 Drawable 的地方。
2. Drawable → Bitmap
规则变成图片
-
如果是
BitmapDrawable,直接拿出 bitmap:val bitmap = (drawable as BitmapDrawable).bitmap -
其它类型(比如 ShapeDrawable、VectorDrawable):
- 创建一个和 drawable 一样大小的 Bitmap 和 Canvas
- 设置 bounds
- draw 到这个 Canvas 上
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) drawable.setBounds(0, 0, width, height) drawable.draw(canvas)
本质:
- Bitmap 负责存储像素(内容)。
- Drawable 负责描述如何绘制(规则)。
- “转换”其实就是把内容和规则结合起来,按需生成对象。
Drawable 和 View 的区别
| 特性 | Drawable | View |
|---|---|---|
| 定义 | 绘制规则封装(只管怎么画,不处理交互和布局) | UI 组件(能画、能布局、能处理交互) |
| 用途 | 作为图标、背景、动画、形状等 | 构建界面元素、控件容器、响应交互 |
| 事件响应 | 不能响应(自身没事件分发能力) | 能直接响应触摸、点击等事件 |
| 生命周期 | 只有 setBounds、draw(简单) | onMeasure、onLayout、onDraw、事件等(复杂) |
| 性能 | 轻量、高效,不占 UI 层级 | 较重,涉及测量、布局和事件,性能略低 |
| 依赖关系 | 依赖 Canvas 或绑定到 View 上 | 直接存在于 UI 层级 |
| 常见场景 | 画 icon、shape、渐变、动画、View 背景 | 各种 UI 控件、布局、交互 |
重点说明
- Drawable 是“绘图规则”,没有自己的像素,也没有独立显示能力,不能处理事件,不能参与布局。
- View 是“UI组件”,可以响应事件、参与布局,内部可通过 Drawable 进行绘制。
- Drawable 画到屏幕上通常依赖 View 的
onDraw(Canvas)或者直接用 Canvas。
自定义 Drawable 的用法
-
实现自定义 Drawable
-
必须继承 Drawable,并重写下面几个方法:
draw(Canvas)setAlpha()(如需透明度,建议顺便实现getAlpha())setColorFilter()(如支持颜色变化)getOpacity()
-
-
举例:
class CustomDrawable : Drawable() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) override fun draw(canvas: Canvas) { paint.color = Color.RED canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), 50f, paint) } override fun setAlpha(alpha: Int) { paint.alpha = alpha } override fun setColorFilter(filter: ColorFilter?) { paint.colorFilter = filter } override fun getOpacity(): Int = PixelFormat.TRANSLUCENT } -
用途:
- 需要在多个自定义 View 中复用一套绘制代码,直接把 Drawable 拿出来用,不用到处复制代码。
- 比如:股票 K 线图、通用自定义控件的底层绘制。
Drawable 的动态特性与性能优势
- 动态绘制:可以根据状态或数据实时改变内容(如 StateListDrawable、ShapeDrawable、GradientDrawable)。
- 省内存:通过矢量图、代码绘制,减少 bitmap 资源消耗。
- 分辨率自适应:VectorDrawable 不怕分辨率变化,适合多屏幕密度场景。
Bitmap 和 Drawable 用法总结
- Bitmap 适合处理底层像素数据,比如图像编辑、像素操作。
- Drawable 适合做 UI 绘制规则封装,比如 shape、动画、状态切换。
- View 适合处理完整 UI 交互,包括布局、响应触摸、复用 Drawable 等。
小结
- 需要“画什么”就用 Drawable(或者 Bitmap)。
- 需要“能被点击/滑动/布局/管理子元素/复杂 UI”就用 View。
- 二者各司其职,掌握各自的职责和局限性即可灵活运用。
学后测验
一、单选题
-
下列关于
Bitmap的描述,哪一项是正确的?- A. Bitmap 仅仅存储绘制规则
- B. Bitmap 可以直接响应用户事件
- C. Bitmap 是像素矩阵,存储具体的颜色数据
- D. Bitmap 一定是矢量图形
答案:C
解析:Bitmap 是像素矩阵,直接记录每个像素的颜色,其他选项均错误。 -
关于
Drawable的本质,下列哪项描述最准确?- A. Drawable 只能用来绘制 Bitmap
- B. Drawable 本质是绘制规则,可以动态生成内容
- C. Drawable 必须有具体像素信息
- D. Drawable 只能用作背景
答案:B
解析:Drawable 代表一套绘制规则,不一定绑定具体像素,也可动态生成。
二、多选题
-
下列哪些属于 Drawable 的典型类型?(多选)
- A. BitmapDrawable
- B. GradientDrawable
- C. StateListDrawable
- D. AnimationDrawable
- E. PaintDrawable
答案:A、B、C、D
解析:E 不是标准 Android Drawable,其他都是常用 Drawable 类型。 -
关于 Drawable 和 View 的区别,下列哪些说法是正确的?(多选)
- A. Drawable 只负责绘制,不负责事件分发
- B. View 既能绘制内容也能响应用户交互
- C. Drawable 有独立的布局和测量逻辑
- D. View 作为 UI 组件,参与布局系统
答案:A、B、D
解析:C 错,Drawable 不参与布局和测量。
三、判断题
-
( ) 你可以通过
BitmapDrawable.getBitmap()方法,将任何 Drawable 转换成 Bitmap。答案:错
解析:只有 BitmapDrawable 支持直接 getBitmap(),其他类型 Drawable 需要手动用 Canvas 绘制到 Bitmap。 -
( ) VectorDrawable 能够自适应不同屏幕密度,通常比 BitmapDrawable 更节省内存。
答案:对
解析:矢量图形天然分辨率无关、内存占用低。 -
( ) Drawable 必须绑定到 View 上才能绘制到屏幕。
答案:错
解析:Drawable 可以直接 draw 到 Canvas(比如自定义 View 的 onDraw 内部)。
四、简答题
-
简述 Drawable 转 Bitmap 的常见实现方法,以及为什么有些 Drawable 不能直接 getBitmap()。
参考答案:
常用做法是:-
- 创建一个 Bitmap 对象和对应的 Canvas。
-
- 调用 Drawable.setBounds() 设置绘制区域。
-
- 使用 Drawable.draw(Canvas) 方法把内容绘制到 Canvas(即 Bitmap)。
-
- 返回得到的 Bitmap。
只有 BitmapDrawable 内部有 bitmap 字段,其他 Drawable(如 ShapeDrawable、GradientDrawable)是“规则”,不是像素数组,不能 getBitmap(),必须通过绘制获取。
- 返回得到的 Bitmap。
-
五、编程题
-
请写出一段 Kotlin 代码,将任意 Drawable 转换为指定宽高的 Bitmap。请简要注释关键步骤。
参考答案:
fun drawableToBitmap(drawable: Drawable, width: Int, height: Int): Bitmap { // 1. 新建指定宽高的 Bitmap val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) // 2. 用 Bitmap 创建 Canvas val canvas = Canvas(bitmap) // 3. 设置 Drawable 的绘制区域 drawable.setBounds(0, 0, width, height) // 4. 绘制到 Canvas(即 Bitmap) drawable.draw(canvas) // 5. 返回 Bitmap return bitmap }解析:通过 setBounds、draw,支持所有 Drawable 类型转换成 Bitmap。
六、综合题
-
简述自定义 Drawable 的主要应用场景,以及和自定义 View 相比的优劣。
参考答案:
主要用于:- 代码复用(多个 View 共享同一套绘制逻辑)
- 动态生成形状、图案、渐变、动画
- 状态响应(如 StateListDrawable)
优点:轻量、高效、不参与布局、便于复用
缺点:不能独立响应用户交互,不参与布局系统