Android 里的 Bitmap、Drawable、Mipmap(没写完)
Drawable 是什么?
Drawable
并不是图
Drawable
是一个抽象类,用于表示可绘制对象。本身是一个绘制工具,更像一个只专注于绘制的 View,所以一个 Drawable 显示什么,取决于它绘制什么
1 | package android.graphics.drawable |
-Drawable
提供了一种通用的方式来处理不同类型的可绘制资源,无论是静态图像、矢量图形、渐变或其他形式的绘制内容。它使开发人员可以轻松地切换和管理不同类型的可绘制资源,以便用于UI
设计。
-Android
提供了许多不同类型的Drawable
子类,如BitmapDrawable
(用于显示位图图像)、ShapeDrawable
(用于绘制基本形状)、VectorDrawable
(用于显示矢量图形)等
Bitmap 是什么?
bitmap
又叫位图,在Android
里,它代表一个像素矩阵,含有一个图片 完整的、未压缩的 像素数据。
既然,Bitmap
是一个包含所有像素信息的矩阵,而每个像素所占空间是你固定的,所以————只要不改变 Bitmap 的尺寸,或者说只要不改变分辨率,那么 Bitmap 的占的内存就不会变,也就是说,Bitmap 的内存占用只和图片的尺寸(分辨率)有关
1 | package android.graphics; |
Mipmap 是什么?
Mipmap(多级纹理映射)
是一种用于优化计算机图形中纹理映射的技术。
纹理映射是一种在三维图形渲染中将二维图像(称为纹理)映射到三维模型表面的方法。
Mipmap 是为了提高图形性能和视觉质量而设计的。
Drawable Bitmap 互转???
了解了 Drawable 和 Bitmap 是什么,那么就回到这个老生常谈的问题了,Drawable 和 Bitmap 如何互转???
其实根本没有什么互转,Bitmap 是一个包含像素信息的矩阵,而 Drawable 根本没有像素信息,那么我们如果想让 Bitmap “转为” Drawable 呢?
只能用 Drawable 将Bitmap 画出来了,而 BitmapDrawable 就是这个工具
1 | val bitmapDrawable = BitmapDrawable(resource, bitmap) |
反之,如何将 Drawable “转成” Bitmap 呢?
Drawable 并没有像素信息,但是需要用像素信息将 Bitmap 填满,该怎么办呢?
只能将 Drawable 的信息用 canvas 绘制出来了
1 | fun drawableToBitmap(drawable: Drawable): Bitmap { |
使用 Drawable
通过资源图片创建 Drawable
您可以通过引用项目资源中的图片文件向应用添加图形。支持的文件类型包括 PNG(首选)、JPG(可接受)和 GIF(不建议)
将文件添加到项目的res/drawable/
目录。进入项目后,您可以从代码或 XML 布局中引用图片资源。
注意
在构建过程中,aapt
工具可能会使用无损图片压缩功能自动优化位于res/drawable/
目录中的图片资源。
例如,可以通过调色板将不需要超过 256 种颜色的真彩色 PNG 转换为 8 位 PNG。这样做会生成质量相同但内存占用量更小的图片。
因此,此目录中的映像二进制文件可能会在构建时发生变化。如果您打算以比特流的形式读取图片,进而将其转换为位图,请改为将图片放在res/raw/
文件夹中,aapt
工具不会修改这些图片。
下面kotlin
代码展示两种在代码里直接用 resId 引用资源的方式
1 | val i = ImageView(this).apply { |
警告
项目中的每个唯一资源只能保留一种状态,无论您为其实例化多少个不同的对象都是如此。
例如,如果您实例化同一图片资源中的两个 Drawable 对象,并更改一个对象的属性(例如 alpha),那么另一个对象也会受到影响。
在处理某个图片资源的多个实例时,应执行补间动画
,而不是直接转换 Drawable 对象。
下面xml
代码段展示了如何在XML
布局中向ImageView
添加可绘制资源:
1 | <ImageView |
如需详细了解如何使用项目资源,请参阅应用资源指南
注意
使用图片资源作为可绘制对象的来源时,请确保图片尺寸适合各种像素密度。
如果图片不正确,系统会将其放大以匹配图片,这可能会导致可绘制对象出现伪影。
如需了解详情,请参阅支持不同的像素密度。
通过 XML 资源创建可绘制对象
通过 XML 创建可绘制对象最大的好处就是,你可以设置一些可变化的属性来控制它,这样会更加灵活,也更加方便
使用 XML 定义Drawable
后,将文件保存在项目的res/drawable/
目录中。
以下示例展示了定义从Drawable
继承的 TransitionDrawable
资源的
XML:
1 | <!-- res/drawable/expand_collapse.xml --> |
然后,通过调用Resources#getDrawable()
并传递 XML 文件的资源 ID 来检索并实例化该对象。
所有有inflate()
的 Drawable
子类,都可以在
XML 中定义并由您的应用实例化。
重要提示
继承 Drawable 子类的自定义类,必须重写 getConstantState()方法
。
请参阅下面的自定义可绘制对象中的要点。
以下代码会实例化 TransitionDrawable
并将其设置为ImageView
对象的内容:
1 | val transition= ResourcesCompat.getDrawable( |
提示
TransitionDrawable
继承自 LayerDrawables
,可以让我们在两层图像中设置淡入淡出效果
Shape drawables
Shape Drawable
继承自 Drawable
可以让你通过写代码的方式绘制一个图形
提示
Shape Drawable 并不算是矢量图,因为它可以设置具体的像素大小
由于具有自己的draw()
方法,因此你可以在自定义View
的onDraw()
事件期间调用ShapeDrawable
的draw()
方法。
如下面的代码示例所示:
1 | class CustomDrawableView(context: Context) : View(context) { |
然后就像使用任何其他自定义View
一样
1 | private lateinit var customDrawableView: CustomDrawableView |
1 |
|
NinePatch drawables(.9图片)
NinePatchDrawable
图形是可拉伸的位图图像,可用作视图的背景。
Android
会自动调整图形大小以适应视图的内容。
NinePatch 图像的一个示例是标准 Android 按钮使用的背景,按钮必须拉伸以容纳各种长度的字符串。
NinePatch 图形是标准的 PNG 图像,其中包含额外的 1 像素边框。
它必须与9.png
扩展名一起保存在项目res/drawable/
目录中,并且还需要删除原图像,否则会报重复错误。
绘制方法
- 通过在边框的左侧和顶部绘制一条(或多条)1 像素宽的黑线来指示可拉伸部分(其他边框像素应为完全透明或白色)
- 在右侧绘制一条线,在底部绘制一条线来定义图像的可绘制部分(实际上是填充线)。也就是你设置
text
填充的地方
Custom drawables(自定义 Drawable)
自定义Drawable
类似于 自定义View
下面的代码显示了绘制圆圈的简单自定义Drawable
1 | class MyDrawable : Drawable() { |
Add tint to drawables(为可绘制对象添加色调)
对于 Android 5.0(API 级别 21)及更高版本,您可为位图和定义为 Alpha 蒙版的位图和九宫格着色。
您可以使用颜色资源或解析为颜色资源(例如?android:attr/colorPrimary
)的主题属性为其着色。通常,您只需创建一次这些资源,然后自动为其着色以与您的主题相符。
您可以使用setTint()
方法对BitmapDrawable
、NinePatchDrawable
或VectorDrawable
对象着色。
您还可以使用android:tint
和android:tintMode
属性在布局中设置色调颜色和模式。
详细信息请看样式和主题背景
从图片中萃取突出颜色
Android 支持库包含Palette
类,可让您从图片中提取突出颜色。
您可以将可绘制对象作为Bitmap
加载,并将其传递给Palette
以获取其颜色。如需了解详情,请参阅使用 Palette API 选择颜色。
Vector drawables(矢量图)
- Vector drawable 是一种矢量图形,在 XML 文件中定义为一组点、线条和曲线及其关联的颜色信息。
- Vector drawable 可缩放,这意味着,可在不降低显示质量的情况下调整大小。因此,它们是 Android 应用的理想选择,有助于缩减 APK
文件的大小并提升性能。 - 您可以在 Android Studio 中创建矢量可绘制对象,方法是右键点击项目中的可绘制对象文件夹,然后依次选择“New”>“Vector
Asset”。您还可以将 SVG 文件作为矢量可绘制对象导入 Android Studio。
关于 VectorDrawable 类
VectorDrawable
定义静态可绘制对象。与 SVG 格式类似,每个矢量图形被定义为一个树层次结构,由path
和group
对象组成。每个path
都包含对象轮廓的几何图形,group
包含转换的详细信息。所有路径的绘制顺序均与它们在 XML 文件中显示的顺序相同。
Vector Asset Studio 工具提供了一种将矢量图形作为
XML 文件添加到项目中的简单方法。
示例
以下VectorDrawable
XML 文件示例会渲染充电模式下电池的图片。
1 | <!-- res/drawable/battery_charging.xml --> |
此 XML 会渲染以下图片:
关于 AnimatedVectorDrawable 类
AnimatedVectorDrawable
用于为矢量图形的属性添加动画。您可以将动画矢量图形定义为三个单独的资源文件或定义整个可绘制对象的单个
XML 文件。
为了更好地理解,我们来看看这两种方法:多个 XML 文件
和单个 XML 文件。
多个 XML 文件
使用此方法,您可以定义三个单独的 XML 文件:
- 一个
VectorDrawable
XML 文件。
-AnimatedVectorDrawable
XML 文件,用于定义目标VectorDrawable
、要添加动画效果的目标路径和组、属性,以及定义为ObjectAnimator
对象或AnimatorSet
对象的动画。 - 一个 Animator XML 文件。
示例
The following XML files demonstrate the animation of a vector graphic.
以下 XML 文件演示矢量图形的动画。
- VectorDrawable 的 XML 文件:
vd.xml
1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" |
- AnimatedVectorDrawable 的 XML 文件:
avd.xml
1 | <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" |
- AnimatedVectorDrawable 的 XML 文件中使用的 Animator XML 文件:
rotation.xml
以及path_morph.xml
1 | <objectAnimator |
1 | <set xmlns:android="http://schemas.android.com/apk/res/android"> |
单个 XML 文件
通过使用此方法,可以通过 XML 捆绑格式将相关的 XML 文件合并到单个 XML 文件中。
在构建应用时,aapt
标记会创建单独的资源,并在动画矢量中引用它们。
此方法需要生成工具 24 或更高版本,并且输出向后兼容。
示例
1 | <animated-vector |
矢量可绘制对象向后兼容解决方案
为了在运行平台版本低于 Android 5.0(API 级别 21)的设备上支持矢量可绘制对象和添加动画效果的矢量可绘制对象,或使用低于
Android 7.0(API 级别 24)的fillColor
、fillType
和strokeColor
功能,VectorDrawableCompat
和AnimatedVectorDrawableCompat
可通过两个支持库分别通过support-vector-drawable
和animated-vector-drawable
获得。
Android Studio 1.4 通过在构建时生成 PNG 文件引入了对矢量可绘制对象的有限兼容性支持。不过,矢量可绘制对象和动画矢量可绘制对象支持库既灵活又具有广泛的兼容性,它是一个支持库,因此您可以在低至
Android 2.1(API 级别 7 及更高级别)的所有 Android 平台版本中使用它。如需将应用配置为使用矢量支持库,请将vectorDrawables
元素添加到应用模块的build.gradle
文件中。
使用以下代码段配置vectorDrawables
元素:
1 | // For Gradle Plugin 2.0+ |
1 | // For Gradle Plugin 1.5 or below |
您可以在搭载 Android 4.0(API 级别 14)及更高版本的所有设备上使用VectorDrawableCompat
和AnimatedVectorDrawableCompat
。
Android 加载可绘制对象的方式(而不是每个接受可绘制对象 ID 的地点(例如 XML 文件中)都支持加载矢量可绘制对象。
android.support.v7.appcompat
软件包添加了许多功能,以便轻松使用矢量可绘制对象。
首先,当您将android.support.v7.appcompat
软件包与ImageView
或ImageButton
和FloatingActionButton
等子类一起使用时,可以使用新的app:srcCompat
属性来引用矢量可绘制对象以及android:src
可用的任何其他可绘制对象:
1 |
|
要在运行时更改可绘制对象,您可以像以前一样使用
setImageResource()
方法。要将矢量可绘制对象集成到应用中,使用AppCompat
和app:srcCompat
是最万无一失的方法。支持库 25.4.0 及更高版本支持以下功能:
- 路径变形(PathType 评估器):用于将一条路径变形为另一个路径。
- 路径插值:用于定义灵活的插值器(表示为路径),而不是系统定义的插值器(如 LinearInterpolator)。
支持库 26.0.0-beta1 及更高版本支持以下功能: - 沿路径移动:几何图形对象可以在动画中沿着任意路径四处移动。
使用支持库的多个 XML 文件的示例
以下 XML 文件演示了使用多个 XML 文件为矢量图形添加动画效果的方法。
- VectorDrawable 的 XML 文件:
vd.xml
1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" |
- AnimatedVectorDrawable 的 XML 文件:
avd.xml
1 | <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" |
- Animator XML file that is used in the AnimatedVectorDrawable’s XML file:
rotation.xml
1 | <objectAnimator |
以下 XML 文件演示了使用单个 XML 文件为矢量图形添加动画效果的方法。构建应用时,aapt
标记会创建单独的资源,并在动画矢量中引用这些资源。此方法需要
Build Tools 24 或更高版本,并且输出可向后兼容。
- 单个 XML 文件的示例
1 | <animated-vector |
使用 Bitmap
位深和色深
首先我们需要理解两个概念:位深和色深
可以看到其实这是一个非常有歧义的概念
重要提示
通常说 8bit 图指的是每个颜色通道占 8 位,也就也是
如果是 RGB 格式,那总共就占 3×8=24 位,RGBA 格式总共就占 4×8=32 位
不用太过于纠结这两个概念,只要能清楚描述每个像素的占用就行
包括买显示器的时候也要注意,不要看话术,关键在于像素排列,每个像素占几位(也就是色彩范围)
接下来我们看看 Bitmap 的创建,处理,序列化
存储格式
就位图的存储格式而言,我们会想到 PNG,JPG
当然,Android 也确实给了我们这些格式
1 | // android.graphics.Bitmap |
枚举值 | Bitmap.CompressFormat |
---|---|
JPEG |
压缩为 JPEG 格式。 |
PNG |
Compress to the PNG format. 压缩为 PNG 格式。 |
WEBP |
此字段在 API 级别 30 中已弃用。赞成更明确的CompressFormat#WEBP_LOSSY 和CompressFormat#WEBP_LOSSLESS .* |
WEBP_LOSSLESS |
压缩为 WEBP 无损格式。 |
WEBP_LOSSY |
压缩为 WEBP 有损格式。 |
色彩格式
同样,图片他肯定会有色彩格式,什么 256色,8位色,RGB,RGBA
这个 Android 肯定也给了
1 | public enum Config { |
枚举值 | Bitmap.Config |
---|---|
ALPHA_8 |
每个像素都存储为单个半透明 (alpha) 通道,每个像素占一个字节。 |
ARGB_4444 |
RGBA 组成,每部分占 4 位,共 16 位。每个像素占 2 字节此字段在 API 级别 29 中已弃用。由于此配置的质量较差,建议改用ARGB_8888 。* |
ARGB_8888 |
RGBA 组成,每部分占 8 位,共 32 位。每个像素占 4 字节。 |
HARDWARE |
特殊配置,当位图仅存储在图形内存中时。 |
RGBA_1010102 |
每个像素占 4 字节。 |
RGBA_F16 |
每个像素占 8 字节。 |
RGB_565 |
每个像素占 2 字节,仅对 RGB 通道进行编码:红色 5 位,绿色 6 位,蓝色 5 位。 |
一般情况下,我们不会使用 ALPHA_8,他只存储透明度,没啥用处。
对于 ARGB_4444,它的画质又太感人了。
ARGB_8888 画质高但是太占内存。
RGB_565 还行,就是不可以设置透明度。
注意以下三点即可:
- 一般情况下用 ARGB_8888 格式存储 Bitmap
- ARGB_4444 画面惨不忍睹,被弃用
- 假如对图片没有透明度要求,可以使用 RGB_565,比 ARGB_8888 节省一半的内存开销
创建方法
BitmapFactory
可见 BitmapFactory 有很多方便我们将图片加载成 Bitmap 的方法,并且还有一个 Options 类来方便我们进行一些其他的配置
接下来我们看看 bitmapFactory.Options
可以看到有很多成员变量,根据前缀可以将他们分为,in 开头和 out 开头
in 前缀:这些成员变量是用作输入参数,在调用 decode 方法时,你可以设置这些选项来影响图像的加载方式。
例如,inSampleSize 用于指定图像的采样率,inPreferredConfig 用于指定图像的颜色配置,inJustDecodeBounds 用于仅获取图像的尺寸信息而不加载整个图像等等。这些选项用于控制图像的加载行为和内存使用情况。
out 前缀:这些成员变量在调用 decode 方法后会被填充和更新,用于返回有关加载图像的信息。
例如,outWidth 和 outHeight 用于存储解码后图像的宽度和高度信息,outMimeType 用于存储解码后图像的 MIME 类型,outColorSpace 用于存储解码后图像的色彩空间等等。这些选项用于获取解码后图像的信息而无需加载整个图像。
Bitmap
这里的方法都是创建空白的 Bitmap 或者需要自己绘制的 Bitmap
压缩
计算 Bitmap 大小
我们加载这个漂亮的(实际文件大小是 192309 字节)
根据我们的了解,如果我们加载这张图片, 应该是 1920 × 1080×4 = 8294400 byte
我们计算验证一下
1 | val bitmap = BitmapFactory.decodeResource(resources, resId) |
1 | bitmap=62726400 |
这三个数是怎么来的呢?
首先宽度和高度,需要经过 dp 和 px 的换算,获取 desity 为 2.75,而这个 density 就是 Bitmap 的密度,当然你也可以自定义密度,但默认用的是系统自己的密度
宽度 = 1920×2.75 = 5280
高度 = 1080×2.75 = 2970
bitmap 的 ByteCount = 4 × 宽度 × 高度 = 62726400(4 是因为 ARGB_8888 一个像素占 4字节)
62726400 byte ≈ 59.82 MB
可以看到,62726400 ÷ 192309 ≈ 326.175 加载这个图片会直接放大 326 倍
这个占用可以说是非常恐怖了
质量压缩
对于一张图片,我们在不改变分辨率的情况下,改变它的像素的占用,或者将效果不明显的像素删除以方便压缩至更小体积,这就是质量压缩
质量压缩不会改变 Bitmap 的内存占用(因为 Bitmap 的内存占用只和尺寸有关),但会改变序列化到硬盘里的图片文件大小
1 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
尺寸压缩
尺寸压缩,顾名思义,就是将尺寸缩小(或者说分辨率降低),以此来压缩图片
尺寸压缩会直接缩小 Bitmap 的内存占用,也会压缩输出到硬盘的图片文件大小
- 改变 inSampleSize
1 | fun decodeSampleBitmapFromResource( |
- 改变 inDensity
这个变量就是改变 Bitmap 密度的
如果你将
- Title: Android 里的 Bitmap、Drawable、Mipmap(没写完)
- Author: lucas
- Created at : 2023-11-07 11:14:23
- Updated at : 2024-11-12 09:01:29
- Link: https://darkflamemasterdev.github.io/2023/11/07/Android-里的-Bitmap-Drawable-Mipmap/
- License: This work is licensed under CC BY-NC-SA 4.0.