(2019.5.29)关于drawable你可能不知道的一些事

(转载公司内部论坛本人文章2019.5.29)
导读: Android项目中,官方建议将不同分辨率的资源图片分别放在drawable-mdpi,drawable-hdpi,drawable-xhdpi等不同的drawable文件夹下,以便不同分辨率的手机加载不同的图片资源,从而实现不同手机上图片显示大小一致。那么,不同的手机是根据什么来决定加载哪个drawable文件夹下的资源图片呢?加载不同drawable文件夹下的图片对图片宽高和内存会有影响吗?假设手机对应的drawable文件夹下没有该图片资源,又该怎样加载呢?

首先,Android手机是根据屏幕的每英寸像素(dpi值)来决定加载哪个drawable文件夹下的图片资源的,这个相信大家都已经知道了。可以通过下面代码查看屏幕的dpi值

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

其中xdpi代表屏幕宽度的dpi值,ydpi代表屏幕高度的dpi值,通常这两个值几乎相等或及其接近。我用小米note1**(系统版本为android6.0,分辨率为1080*1920)**测得两个值分别是386.366和387.047。对应加载的drawable文件夹,可以参考下表:

dpi范围密度类型对应的density值
0dpi ~ 120dpildpi0.75
120dpi ~ 160dpimdpi1
160dpi ~ 240dpihdpi1.5
240dpi ~ 320dpixhdpi2
320dpi ~ 480dpixxhdpi3
480dpi ~ 640dpixxxhdpi4

所以,小米note1的387dpi属于320dpi ~ 480dpi范围,会加载drawable-xxhdpi文件夹下的图片资源。
下面我做一个试验。准备一张412 * 536分辨率的图片ic_android,放在drawable-xxhdpi文件夹,然后布局引用这张图片。
在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取图片信息"
        />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_android"
        />
</LinearLayout>

代码很简单,运行一下程序,得到显示效果如下所示:
在这里插入图片描述

小米note1的屏幕宽度分辨率是1080px,图片宽度是412px,所以图片宽度大约占据屏幕宽度的2/5左右,跟预期效果差不多。
接着,我将ic_android图片移动到drawable-xhdpi。注意,是移动不是复制。运行程序看一下效果。
在这里插入图片描述
图片反而变大了?接着,再将图片移动到drawable-mdpi。运行程序,效果如下:
在这里插入图片描述

此时图片已经铺满了整个屏幕。将图片放在低dpi的drawable文件夹下,加载出来的图片反而变大了?抱着好奇的心理,用代码打印出每种情况下,图片加载完成后的宽高信息:

ImageView imageView = findViewById(R.id.image_view);
BitmapDrawable bitmapDrawable = (BitmapDrawable) imageView.getDrawable();
Bitmap imageBitmap = bitmapDrawable.getBitmap();
Logger.i("wujunxuan", "imageWidth = " + imageView.getWidth() + ", imageHeight = " + imageView.getHeight());
Logger.i("wujunxuan", "bitmapWidth = " + imageBitmap.getWidth() + ", bitmapHeight = " + imageBitmap.getHeight() + ", size = " + imageBitmap.getAllocationByteCount() + "B");

xxhdpi
xhdpi
mdpi

可以看到,图片放在低dpi的drawable文件夹下,加载后的图片宽高是等比例放大的,并且内存占用也会变得很大,mdpi居然是xxhdpi内存的9倍,这无疑是一个巨坑啊。留意三种情况下bitmap的width或height比例,刚好是1:1.5:3。这个缩放比例是怎么确定的呢?
这时候,下意识的上网找资料。很遗憾,网上找了很久,没有找到相关的介绍资料。那只能查看源码了,看着看着,发现这块处理的相关代码AssetManager属于native层,是用C++写的,查阅起来比较麻烦,看了很久都没有找到对应代码。(这块找到源码的同学欢迎随时找我交流_
既然有比例数据,并且比例是有规律的,我们不妨从试验的数据上下手。细心观察,其实很容易发现其中的缩放逻辑。留意到文章开头那个表格,drawable-mdpi,drawable-xhdpi,drawable-xxhdpi对应的density值分别为1,2,3,我们一般也在这几个文件夹中存放所谓的1倍,2倍,3倍图。图片资源放在drawable-xhdpi中,系统默认该资源图片是2倍图,先将图片宽高除于2倍,然后再乘于手机自身的density值,这个逻辑也保证了不同分辨率的手机上,图片显示大小是接近一致的。即:

bitmap的宽(高) = 图片宽(高)分辨率  / 对应drawable文件夹的density值  * 手机自身的density值

上面的例子,通过代码查看到小米note1手机自身的density值是2.75dpi(非官方标准的3dpi),图片的宽是412,利用公式计算三个drawable文件夹下图片的宽:

//xxhdpi
412 / 3 * 2.75 = 378
//xhdpi
412 / 2 * 2.75 = 567
//mdpi
412 / 1 * 2.75 = 1133

计算出来的数值和上面的测试数值一致。可以证明那个计算公式是正确的。
排除机型的偶然性,拿另外一台手机(小米8)做同样的测试,将ic_android资源图片分别移动到三个drawable文件夹下。小米8的系统版本为android 9.0,分辨率为1080 * 2248,xdpi为403.411,ydpi为402.107,density为2.75。试验得到图片相关信息如下:
xxhdpi
xhdpi
mhdpi

果然不同的系统版本还真有点不同。发现一个有趣的现象,图片显示后,ImageView的宽高依然遵循上面的公式(手机屏幕宽度为1080px,所以ImageViev宽度最大为1080),但是bitmap的宽高在drawable-mdpi,drawable-xhdpi文件夹下却保持了图片本身的宽高(分辨率)。猜想,这个可能是Android在高系统版本做的一个优化。在小米note1中,图片自身分辨率是412 * 538,放在低dpi的drawable文件夹下,图片加载后的bitmap宽高大于图片自身的分辨率,但对于图片显示清晰度并没有提升,而Android中Bitmap的内存占用大小跟Bitmap的宽高是正比关系,这反而增加了内存占用。所以完全可以只缩放ImageView的宽高,而bitmap的宽高保持和图片自身一致,这样可以大大节省内存占用。如上面试验,小米note1在三个文件夹下的内存占用比是1:2.25:9,而在小米8上,内存占用是保持不变的。
另外,假设手机对应的drawable文件夹下没有该图片资源,又该怎样加载呢?这个比较好验证,只要在所有drawable文件夹下都放置同名称图片,然后依次删掉验证即可。得到的结论是:如果在对应的dpi文件夹下没有找到图片资源,系统会优先去更高密度的文件夹下找这张图片。例如,小米note1在drawable-xxhdpi没有找到该图片资源,会依次从drawable-xxxhdpi->drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi->drawable-nodpi->drawable文件夹里面找。
总结:
在我们项目中,为了减少apk包大小,只用一套图片资源二倍图,放在drawable-xhdpi下,这样做明显是不好的,既达不到手机本身的显示效果,还可能增加内存占用。目前市面上Android手机的分辨率基本上都是1080以上,dpi值基本都达到了xxhdpi,所以最佳放置图片资源的文件夹就是drawable-xxhdpi,这样可以保证最佳的显示效果,也可以减少一些Bitmap的内存占用。虽然这样会增加apk的包大小,但android可以使用webp格式的图片,事实上从xhdpi升级到xxhdpi并不会增加太多的包大小。
以上有哪些地方写得不对的,欢迎指出。_

END

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值