Android中的drawable像素密度适配详解

本文大纲:

  • 1.为什么要分drawable-xxx目录?
  • 2.Bitmap的介绍
  • 3.不同drawable-xxx中的图片内存测试

1.为什么要分drawable-xxx目录

 

android项目资源中对于drawable文件夹可以分为以下类型的文件夹:

  • drawable-ldpi(低密度) 
  • drawable-mdpi(中等密度)     
  • drawable-hdpi(高密度)
  • drawable-xhdpi(超高密度)    
  • drawable-xxhdpi(超超高密度)
  • drawable-xxxhdpi(超超超高密度)
  • drawable-nohdpi(无缩放)   
  • drawable(默认密度)

 

我们大概知道这些是存放不同分辨率的图片的,高分辨率的图片要放在高密度的文件夹里,低分辨率的图片要放在低密度的文件夹里。但是考虑下面几个问题:

  1. 各个不同的文件夹对应的具体密度是多少?
  2. 各个文件夹该放置多大分辨率的图片?
  3. android加载图片的时候是怎么从这么多drawable文件夹里找到图片的?

问题1:各个文件夹对应的具体密度

表1:

Android中的drawable像素密度适配详解_第1张图片

注意密度是根据设备来决定的,设备密度的获取方式:

float xdpi = context.getResources().getDisplayMetrics().xdpi; 

float ydpi = context.getResources().getDisplayMetrics().ydpi;

假设此时的设备密度是480dpi, 那么xxhdpi会被认为是程序的匹配密度目录,默认优先使用xxhdpi文件下的图片。

 

问题2:各个文件夹该放置多大分辨率的图片:

每种密度下的图片设计成什么尺寸Android也是给出了建议:

表2:

Android中的drawable像素密度适配详解_第2张图片

上面是andriod给出的建议尺寸,实际中可能会不同。但是一定要记住一下几点:

 

  • 如果图片所在目录的dpi低于匹配目录,那么该图片会被人为是为低密度设备需要的,现在要显示在高密度设备上,图片会进行放大。
  • 如果图片所在目录的dpi高于匹配目录,那么该图片会被认为是为高密度设备需要的,现在要显示在低密度设备上,图片会进行缩放。
  • 如果图片所在目录为drawable-nodpi,则无论设备dpi为多少,保留原图片大小,不进行缩放

 

延伸问题:如果不匹配,那么缩放或者放大多少呢?

 

公式: scale=设备dpi / 图片所在drawable目录对应的最大dpi

 

举个例子,假设当前的设备密度是 480dpi, 一张72x72的图片分别放在drawable-xxhdpi和drawable-xhdp,两种情况下图片各占多少内存?(假设现在是RGB565格式)

 

分析:

1.图片放在drawable-xxhdpi:

由表1可知xxhdpi对应的最大dpi为480dpi

scale = 设备密度/xxhdpi对应的最大dpi = 480dpi/480dpi = 1

也就是说这种情况下不会进行缩放,因为图片恰好放在了设备匹配的文件夹中。此时图片占用的内存= 72 * 72 * 2 = 10368

 

2.图片放在drawable-xhdpi:

由表1可知xhdpi对应的最大dpi为320dpi

scale = 设备密度/xhdpi对应的最大dpi = 480dpi/320dpi =1.5

这种情况下要进行放大,因为低于设备匹配目录,此时图片占用的内存= 72 * 72 * 2 * 1.5 = 15552

 

所以,我们知道,如果在开发时不恰当的将高分辨的图片放在低密度的文件夹里,很可能在高密度的设备上显示的时候会oom,因为它们会进行放大!!

问题3:Android加载drawable文件夹中的图片的过程

图1:

Android中的drawable像素密度适配详解_第3张图片

查找步骤:

  • 1.计算出设备的密度,并找到与屏幕密度匹配的drawable目录,根据表1可得
  • 2.首先去匹配的drawable文件夹里找图片,如果没有,先去更高密度的drawable中查找,如果依然找不到,再去更高密度drawable中找。
  • 3.如果没有密度更高的drawable文件夹,就去nodpi文件夹查找。
  • 4.如果nodpi文件夹没有,就去匹配密度的低级别的drawable文件夹依次查找。
  • 5.如果所有drawable都找不到,就报错。

说明:

  1. 以上几步,每一步如果找到,就会立即返回,不会再往下查找。
  2. 只有当图片在设备密度匹配的drawable文件夹或者nodpi文件夹中时,图片不会进行缩放。

Bitmap的介绍:

简单介绍bitmap, 它是android的图片资源或文件在内存中的展现方式。

 

颜色通道格式:

/**
*每个像素存储为一个半透明(alpha)通道。
*这对于有效地存储掩码非常有用。
*不存储颜色信息。
*使用这种配置,每个像素需要1字节的内存。
* /
Bitmap.Config.ALPHA_8

/ * *
*每个像素存储在2个字节,只有RGB通道
*编码:红色是存储5位精度(32可能
*值),绿色是存储与6位精度(64可能
*值)和蓝色是存储与5位精度。
*
*根据不同的情况,此配置可能会产生轻微的可视工件
*关于源的配置。例如,没有
*犹豫不决,结果可能会显示出一抹绿色。得到更好的
*结果应用抖动。
*
*此配置在使用不透明位图时可能很有用
*不需要高的色彩保真度。
*
* 

使用此公式压缩成16位:

* < pre类= " prettyprint " > *短颜色= (R & 0 x1f) < < 11 | (G & 0 x3f) < < 5 | (B & 0 x1f); * < / pre > Bitmap.Config.RGB_565 / * * *每个像素存储在2个字节。三个RGB彩色通道 *和alpha通道(半透明)存储为4位 精度(16个可能的值) * *如果应用程序需要,此配置非常有用 *存储半透明信息,但也需要保存 *内存。 * *建议使用{@link #ARGB_8888}代替这个 *配置。 * *注意:在{@link android.os.Build.VERSION_CODES#KITKAT}时, *使用此配置创建的任何位图都将被创建 *使用{@link #ARGB_8888}代替。 * * @deprecated,因为这个配置的质量很差, *建议使用{@link #ARGB_8888}。 * / Bitmap.Config.ARGB_4444 / * * *每个像素存储4个字节。每个频道(RGB和alpha) *用于半透明)以8位精度存储(256位) *可能值。) * 这种配置非常灵活,提供了最好的 *质量。只要可能就应该使用它。 * *

使用这个公式打包成32位:

* < pre类= " prettyprint " > * int颜色= (& 0 xff) < < 24 | (B & 0 xff) < < 16 | (G & 0 xff) < < 8 | (R & 0 xff); * < / pre > * / Bitmap.Config.ARGB_8888 / * * *每个像素存储在8个字节。每个频道(RGB和alpha) *用于半透明)存储为a * {@link android.util。半精度浮点值}。 * *此配置特别适合宽色域和 * HDR的内容。 * *

使用这个公式压缩成64位:

* < pre类= " prettyprint " > * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff); * < / pre > * / Bitmap.Config.RGBA_F16 / * * *特殊配置,当位图只存储在图形内存。 该配置中的位图总是不可变的。 * *这是最佳的情况下,当与位图的唯一操作是绘制它在a *屏幕上。 * / Bitmap.Config.HARDWARE

在加载图片的时候默认使用ARGB_8888,可以指定颜色格式,通过BitpamOption设置

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = ARGB_4444;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), img, options);

Bitmap的信息:

// 一行占用多少像素
int width = bitmap.getWidth();
// 一列占用多少像素
int height = bitmap.getHeight();
// 一行占用多少字节
int rowBytes = bitmap.getRowBytes();
// 可用于存储此位图像素的最小字节数 它等于 rowBytes * height
int byteCount = bitmap.getByteCount();
// 用于存储位图像素的已分配内存的大小。
int allocationByteCount = bitmap.getAllocationByteCount();

说明:

  • width和height是像素, 比如 72*66的图片,width和height是 72 和 66. 不过当图片没有在设备密度匹配drawable目录中的时候,会进行相应的缩放。
  • rowBytes=width* 每个像素所占字节, 每个像素所占的字节是由图像的颜色通道决定的。
  • byteCount 和 allocationByteCount 默认情况下是相同的。但是如果一个位图被重用或者手动重新配置之后,那么就会不同。

不同drawable-xxx中的图片内存测试

 

基于上面的基础知识,我们密度为480dpi的设备上,将一张 66x71的图片分别放在drawable不同密度的文件夹中,进行内存占用情况的测试。

推测:

    图片放在drawable-xxhdpi或者drawable-nodpi中时内存占用为原始大小,其他情况会缩放。

验证:

    首先来编写测试的代码:

// 获取设备的dpi
float xdpi = context.getResources().getDisplayMetrics().xdpi;
float ydpi = context.getResources().getDisplayMetrics().ydpi;
// 加载图片查看内存
LogUtil.E("=====测试,设备密度: xdpi:"+xdpi+",ydpi:"+ydpi+", 使用ARGB8888模式加载66x71的图片======");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), img, options);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int rowBytes = bitmap.getRowBytes();
int byteCount = bitmap.getByteCount();
int allocationByteCount = bitmap.getAllocationByteCount();
LogUtil.E("加载结果:");
LogUtil.E("width-->" + width);
LogUtil.E("height-->" + height);
LogUtil.E("rowBytes-->" + rowBytes);
LogUtil.E("byteCount-->" + byteCount);
LogUtil.E("allocationByteCount-->" + allocationByteCount);

 

图片放在 drawable下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->198
height-->213
rowBytes-->792
byteCount-->168696
allocationByteCount-->168696

图片放在 drawable-mdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->198
height-->213
rowBytes-->792
byteCount-->168696
allocationByteCount-->168696

图片放在 drawable-xhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->99
height-->107
rowBytes-->396
byteCount-->42372
allocationByteCount-->42372

图片放在 drawable-xxhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->66
height-->71
rowBytes-->264
byteCount-->18744
allocationByteCount-->18744

图片放在 drawable-xxxhdpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->50
height-->53
rowBytes-->200
byteCount-->10600
allocationByteCount-->10600

图片放在 drawable-nodpi下的输出情况:

=====测试,设备密度: xdpi:480.0,ydpi:480.0, 使用ARGB8888模式加载66x71的图片======
加载结果:
width-->66
height-->71
rowBytes-->264
byteCount-->18744
allocationByteCount-->18744

 

 

 

 

 

 

 

 

你可能感兴趣的:(android)