首页 > 编程知识 正文

android drawable资源,android:src drawable

时间:2023-05-03 20:35:46 阅读:224462 作者:4804

1 问题背景

当在Android Studio来新建一个Android项目,该项目有如下的目录结构:

Android项目的某个模块中的res文件夹下,有很多以drawable或mipmap开头的子文件夹,这些子文件夹都是用来存储图片的,那么他们之间有什么区别呢?

2 Android中drawable或mipmap文件夹存放图片的区别 2.1 结论

先说结论:应用图标的图片资源存放在mipmap系列文件夹中,而其余图片存放在drawable系列文件夹中

2.2 Android官方对drawable和mipmap文件夹用途的描述

drawable文件夹存储bitmap文件(png, jpeg, gif),9-patch文件和xml文件,这些文件用于描述包含多种状态(normal, pressed, focused)的可绘制形状或可绘制对象。

mipmap文件夹用于存放APP的icon图标文件。Android系统会保留这个文件夹中所有的图片资源,而不受到应用安装的设备的屏幕分辨率的影响。这个行为允许启动程序为应用选择最好的分辨率图标显示在主屏幕上。

2.3 mipmap的定义——什么是mipmap

在三维计算机图形的贴图渲染过程中有一个常用的技术被称为Mipmapping。为了加快渲染速度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为 MIP map 或者 mipmap。这个技术在三维游戏中被非常广泛的使用。“MIP”来自于拉丁语 multum in parvo 的首字母,意思是“放置很多东西的小空间”,而mipmap中的map有铺设、贴图的意思。

Mipmap中每一个层级的小图都是主图的一个特定比例的缩小细节的复制品。虽然在某些必要的视角,主图仍然会被使用来渲染完整的细节。但是当贴图被缩小或者只需要从远距离观看时,mipmap就会转换到适当的层级,此时mipmap贴图需要被读取的像素远少于普通贴图,所以渲染的速度得到了提升。而且操作的时间减少了,因为mipmap的图片已经是做过抗锯齿处理的,从而减少了实时渲染的负担。放大和缩小也因为mipmap而变得更有效率。

如果某张mipmap贴图的基本尺寸是256x256像素且长宽特定缩放比例为2,该mipmap贴图就会有8个层级。每个层级是上一层级的四分之一的大小,依次层级大小就是:128x128;64x64;32x32;16x16;8x8;4x4;2x2;1x1(一个像素)。

下图是一个mipmap 如何储存的例子,左边的主图伴有一系列逐层缩小的备份小图:

2.4 Android中mipmap的应用场景

在绝大部分安卓设备的Launcher桌面中,打开或关闭一个APP会有APP打开或关闭的动画效果,例如打开一个APP时会先加载放大该APP的动画,接着便是APP启动界面的放大动画直至填满整个屏幕。可以观看下述的Redmi K30Pro设备打开和关闭APP的动画效果:

那么在这个放大或缩小APP图标的动画中,使用drawable下的图片作为APP图标,那么整个放大或缩小的动画都只会使用这一张图片,例如一张低分辨率的图片的放大动画效果则会失真,而高分辨率的缩小动画效果会耗费CPU效率来缩放图片;如果使用mipmap下的图片,Android系统则会根据动画对图片的缩放程度,来自动选择最接近当前分辨率的图片来做缩放处理,这样就实现了Google官方文档中描述的更好视觉效果和更高效率的目的。

2.5 位图错放密度限定符文件夹的后果

如果有一张1MB大小、3500px*2500px的image.jpg图片,那么它在APP中加载会占用多少内存呢?是不是也是占用1MB的内存?

首先我们要知道默认情况下,一个像素占用4个字节,因为Android默认采用ARGB来表示颜色,例如#FFFF7A25表示完全不透明的橙色。接着我们来看看计算APP中加载图片所占用内存的公式:

APP中加载图片所占用内存=横向像素数*纵向像素数*每个像素占用的内存(默认为4)

因此一张1MB大小、3500px*2500px的image.jpg图片,它在APP中所占用内存为3500*2500*4/1024/1024≈33.3786MB,这么一张大分辨率的图片足以让APP抛出一个OOM内存溢出的异常。

那么为什么一张实际占用空间大小为1MB的图片在APP中加载图片所占用内存为33.3786MB呢,这主要是因为该图片是应用了相应压缩算法的jpg图片,而在Android中使用Bitmap时需要该图片的原始数据,所以要按照上述的公式进行计算。

如果把该图片仅存放于drawable-mdpi文件夹下,而屏幕密度对应为xhdpi的设备在APP中加载该图片会占用约133.5144MB的内存,是原来理论值33.3786MB的四倍,这就更容易导致内存溢出了。这主要是因为Android系统会把drawable-mdpi文件夹下的该图片的宽和高各自扩大2倍,以保证该图片能在屏幕密度对应为xhdpi的设备上正常显示。

再举另一个例子,如果把该图片仅存放且正确地存放于drawable-hxdpi文件夹下,而屏幕密度对应为mdpi的设备在APP中加载该图片会占用约8.3447MB的内存,是原来理论值33.3786MB的四分之一。但是Google仍然不推荐我们只存放xhdpi分辨率下的该图片,而是应该提供mdpi和hdpi分辨率下备用位图,这样屏幕密度对应为mdpi的设备就能直接加载drawable-mdpi文件夹下的该图片,就不用再从drawable-xhdpi文件夹中取出该图片再进行缩小了,因为这个缩小过程也是会消耗系统资源的,这也是为什么Google要求我们为mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi屏幕密度设备提供备用位图的原因。

具体的比例关系如下图所示(NX表示表示宽和高是mdpi的N倍,总内存就是N*N倍):

这也是为什么Android要求备用位图要放在对应密度限定符的文件夹中,而位图错放密度限定符的文件夹可能导致OOM内存溢出、消耗不必要的系统资源等问题。

那么在将仅存在于drawable-xhdpi文件夹下的某图片缩小到适配mdpi屏幕分辨率设备的过程中,一定要将此图片完整地加载进入进内存再进行缩小的过程吗?

当然不是,可以使用BitmapFactory.decode()传入一个Options对象,进行某些设置即可读取到该图片的宽高和类型,而不需要读取整个图片到内存中。读取了图片的宽高属性后,可以按照想要的比例进行缩放读取。我的理解是如果你设置的压缩比是2(横向纵向像素各缩小2倍,总压缩比4倍),在BitmapFactory进行decode的时候知道了你的压缩比,它会选择性的读取某些像素点(具体算法未知,可以简单的想成比如原图是100px*100px,现在我们获得到了它的输入流,只是平均地从输入流中读取50px*50px的像素)。这样就实现了大图片不加载到内存先压缩的过程。

这种做法可以用在本地图片的显示处理,同时也可以用在加载网络图片的时候。都是为了避免OOM,同时实现对大图片进行压缩。

本文参考文献:

维基百科-Mipmap

在 Android Studio 中图片资源应该放置在哪里? - BravianZhao的回答 - 知乎

Stack Overflow-Mipmap drawables for icons-sergej shafarenka的回答

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。