在新建一个Android项目时。在res目录下会自己主动生成几个drawable目录,drawable-ldpi,drawable-mdpi,drawable-hdpi,一直以来都对此不太清楚。图片应该放到哪个目录以下。有什么不同的影响?曾经一直都是干脆再新建一个不带后缀的drawable目录,图片都丢进去,如今决定彻底搞清楚这个事儿。

1、基础知识

density(密度):简单的说就是一个比例系数,用来将Dip(设备独立像素)转换成实际像素px。详细公式是:

px = dip*density+0.5f;

densityDpi:The screen density expressed asdots-per-inch.简单的说就是densityDpi = density*160

drawable目录除了这些密度类的后缀,还有比如-en表示英语环境,-port表示用于竖屏等,这里不做讨论。能够參考http://developer.android.com/guide/topics/resources/providing-resources.html

另附一张官方的屏幕大小与密度的相应表:

 2、为什么要缩放

为了适应这么多乱七八糟的设备,Android官方就建议大家针对不同密度的设备制作不同的图片:

36x36 (0.75x) for low-density

48x48 (1.0xbaseline) for medium-density

72x72 (1.5x) for high-density

96x96 (2.0x) for extra-high-density

180x180 (3.0x) for extra-extra-high-density

192x192 (4.0x) for extra-extra-extra-high-density(launcher icon only; see note above)

问题就来了,假设你不听建议。就整了一种密度的图片呢?那么当遇到不同密度的手机时,系统就会好(无)心(情)的对你的图进行缩放了,按文档的说法,这是为了你的应用更好看。

缩放公式:缩放后大小= 图片实际大小 × (手机密度/图片密度)

当中图片密度由图片所在drawable目录的后缀决定

比方一张100X100的图放在mdpi目录里。在hdpi的手机上,缩放后大小= 100 * (1.5/1) = 150

就成了一张150*150的图片。

3、android:anyDensity

(网上有些博客对这个属性的解释是错的,这里特意提一下)

在AndroidManifest.XML文件中能够设置这么一个属性:<supports-screens android:anyDensity="true"/>

不设置的话默觉得true。

按文档的说法(http://developer.android.com/guide/practices/screens_support.html),这个值假设为true,缩放机制为预缩放(pre-scaling),假设为false,缩放机制为自己主动缩放(auto-scaling),差别是预缩放是在读取时缩放,自己主动缩放时在绘制的时候缩放,从速度来说预缩放要快一些。另外另一个非常重要的差别。就是假设<supports-screensandroid:anyDensity="false"/>,应用在请求屏幕參数时。系统会欺骗应用,告诉它你如今跑在一个density为1的手机上,而无论手机实际density是多少,比方实际手机是hdpi。尺寸480*800,系统会告诉应用屏幕尺寸是320(400/1.5)*533(800/1.5),然后当应用将图片绘制到(10,10)到(100,100)的区域时。系统会将其转换到(15,15)到(150,150),这时假设你去直接操作这些缩放后的图。就会出些不可预期的问题。总之就是建议不要把这个属性设为false。

按我的个人理解,这个false就是告诉系统这个应用不支持多分辨率,于是系统就觉得你仅仅支持默认分辨率(mdpi),系统就会给你虚拟一个mdpi的设备,让你显示在上面,系统再从这上面拉伸或者缩小到实际设备上。这样既速度慢又效果不好。所以就不推荐。

4、各文件夹读取优先级

如果项目内有例如以下drawable文件夹:

drawable

drawable-nodpi

drawable-ldpi

drawable-mdpi

drawable-hdpi

drawable-xhdpi.

(假设不想系统对图片进行缩放,能够把图片放到drawable-nodpi文件夹下,从该文件夹读的图片系统不会进行不论什么缩放。)

(由下文可知。不带后缀的drawable文件夹下的图片依照drawable-mdpi处理.)

假设这些文件夹下都可能有一张同名图片,那系统该读哪一张呢?

毋庸置疑,假设手机密度同样的对应的密度文件夹下有该图片,那就是它了,假设没有呢?

跟踪源代码看看系统是怎样选择图片的(基于android4.4.2):

ImageView.java:

setImageResource()

resolveUri()

Resources.java:

getDrawable()

getValue()

AssetManager.java:

getResourceValue()

native loadResourceValue()

frameworks/base/core/jni/android_util_AssetManager.cpp:

android_content_AssetManager_loadResourceValue()

frameworks/base/libs/androidfw/AssetManager.cpp:

AssetManager::getResources()

AssetManager::getResTable()

frameworks/base/libs/androidfw/ResourceTypes.cpp:

ResTable::getResource()

ResTable::getEntry()

  1. ssize_t ResTable::getEntry(
  2. const Package* package, int typeIndex, int entryIndex,
  3. const ResTable_config* config,
  4. const ResTable_type** outType, const ResTable_entry** outEntry,
  5. const Type** outTypeClass) const
  6. {
  7. ********省略*******
  8.  
  9. const size_t NT = allTypes->configs.size();
  10. for (size_t i=0; i<NT; i++) {
  11. const ResTable_type* const thisType = allTypes->configs[i];
  12. if (thisType == NULL) continue;
  13.  
  14. ResTable_config thisConfig;
  15. thisConfig.copyFromDtoH(thisType->config);
  16.  
  17. ********省略*******
  18.  
  19. if (type != NULL) {
  20. // Check if this one is less specific than the last found. If so,
  21. // we will skip it. We checkstarting with things we most care
  22. // about to those we least care about.
  23. if(!thisConfig.isBetterThan(bestConfig, config)) { //就是这里
  24. TABLE_GETENTRY(ALOGI("Thisconfig is worse than last!\n"));
  25. continue;
  26. }
  27. }
  28.  
  29. type = thisType;
  30. offset = thisOffset;
  31. bestConfig = thisConfig;
  32. TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
  33. if (!config) break;
  34. }
  35. ********省略*******
  36. return offset + dtohs(entry->size);
  37. }

ResTable_config::isBetterThan()

  1. bool ResTable_config::isBetterThan(const ResTable_config& o,
  2.  
  3. const ResTable_config* requested) const {
  4.  
  5. if (requested) {
  6.  
  7. **************
  8.  
  9. if (screenType || o.screenType) {
  10.  
  11. if (density != o.density) {
  12.  
  13. // density is tough. Any density is potentially useful
  14.  
  15. // because the system will scale it. Scaling down
  16.  
  17. // is generally better than scaling up.
  18.  
  19. // Default density counts as 160dpi (the system default)
  20.  
  21. // TODO - remove 160 constants
  22.  
  23. int h = (density?density:160);
  24.  
  25. int l = (o.density?o.density:160);
  26.  
  27. bool bImBigger = true;
  28.  
  29. if (l > h) {
  30.  
  31. int t = h;
  32.  
  33. h = l;
  34.  
  35. l = t;
  36.  
  37. bImBigger = false;
  38.  
  39. }
  40.  
  41. int reqValue = (requested->density?requested->density:160);
  42.  
  43. if (reqValue >= h) {
  44.  
  45. // requested value higher than both l and h, give h
  46.  
  47. return bImBigger;
  48.  
  49. }
  50.  
  51. if (l >= reqValue) {
  52.  
  53. // requested value lower than both l and h, give l
  54.  
  55. return !bImBigger;
  56.  
  57. }
  58.  
  59. // saying that scaling down is 2x better than up
  60.  
  61. if (((2 * l) - reqValue) * h > reqValue * reqValue) {
  62.  
  63. return !bImBigger;
  64.  
  65. } else {
  66.  
  67. return bImBigger;
  68.  
  69. }
  70.  
  71. }
  72.  
  73. ***********
  74. }
  75.  
  76. }
  77.  
  78. return isMoreSpecificThan(o);
  79.  
  80. }

关键部分已用红字标明,在多个drawable下都有同名图片时。一个资源ID相应不止一个图片,在getEntry里面就有一个循环,用isBetterThan()函数在循环里把最合适的图片选出来。

能够看见,假设该图片没有指明density。density就默觉得160。这也是drawable目录下的图片被默觉得mdpi的原因。

在isBetterThan函数里,density是当前资源的密度,o.density是之前的循环中已有的最合适的资源的密度,reqValue则是请求密度。

三个if,

第一个if:假设density和o.density都小于reqValue,那么大的那个比較合适

第二个if:   假设density和o.density都大于reqValue,那么小的那个比較合适

第三个if:   假设reqValue大小在density和o.density之间,先推断

if(((2 * l) - reqValue) * h > reqValue * reqValue)

这个推断大意就是请求密度和较小的密度相差非常小而与较大的一个密度相差非常大。那么就觉得较小的密度更合适。

測试环境: 模拟器+Android4.4.2,当中xh和xxh是用真机+Android4.4.2測的。当中ldpi除Android4.4.2外也用Android2.3.1,hdpi除Android4.4.2外也用了Android2.1,结果并无不同。

測试文件夹:drawable-ldpi,drawable-mdpi,drawable-hdpi,drawable-xhdpi,drawable-nodpi,drawable

測试结果():

怎么drawable-nodpi有时候在前面有时候在后面?附两处源代码你就明确了

frameworks/base/include/androidfw/ResourceTypes.h:

  1. enum {
  2. DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
  3. DENSITY_LOW =ACONFIGURATION_DENSITY_LOW,
  4. DENSITY_MEDIUM =ACONFIGURATION_DENSITY_MEDIUM,
  5. DENSITY_TV = ACONFIGURATION_DENSITY_TV,
  6. DENSITY_HIGH =ACONFIGURATION_DENSITY_HIGH,
  7. DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH,
  8. DENSITY_XXHIGH =ACONFIGURATION_DENSITY_XXHIGH,
  9. DENSITY_XXXHIGH =ACONFIGURATION_DENSITY_XXXHIGH,
  10. DENSITY_NONE =ACONFIGURATION_DENSITY_NONE
  11. };

frameworks/native/include/android/configuration.h:

  1. ACONFIGURATION_DENSITY_DEFAULT = 0,
  2. ACONFIGURATION_DENSITY_LOW = 120,
  3. ACONFIGURATION_DENSITY_MEDIUM = 160,
  4. ACONFIGURATION_DENSITY_TV = 213,
  5. ACONFIGURATION_DENSITY_HIGH = 240,
  6. ACONFIGURATION_DENSITY_XHIGH = 320,
  7. ACONFIGURATION_DENSITY_XXHIGH = 480,
  8. ACONFIGURATION_DENSITY_XXXHIGH = 640,
  9. ACONFIGURATION_DENSITY_NONE = 0xffff,

可见drawable-nodpi文件夹下的图片密度值为0xffff。即65535。带入推断一算,结果正如測试所得。

无图无真相,所以贴一张hdpi环境的測试图:

想要知道会读取到哪张图,能够这样:

  1. TypedValue typedValue = new TypedValue();
  2. getResources().getValue(R.drawable.test,typedValue,true);
  3. //然后typedValue.string的值就是实际读取的图片路径

Android资源图片读取机制的更多相关文章

  1. 插件化框架解读之Android 资源加载机制详解(二)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680Android提供了一种非常灵活的资源系统,可以根据不同的条件提供 ...

  2. xamarin.android 资源图片问题

    在xamarin.android 中,关于图片的资源一般都在Resources.drawable下面,在Resources这个文件夹下面,包含了drawable.drawale-hdpi.drawab ...

  3. 【Android】内存卡图片读取器,图库app

    上一篇<[Android]读取sdcard卡上的全部图片而且显示,读取的过程有进度条显示>(点击打开链接)在真机上測试非常有问题.常常遇到内存溢出.卡死的情况.由于如今真机上的内存上,2G ...

  4. 【Android 界面效果30】Android中ImageSwitcher结合Gallery展示SD卡中的资源图片

    本文主要是写关于ImageSwitcher结合Gallery组件如何展示SDCard中的资源图片,相信大家都看过API Demo 中也有关于这个例子的,但API Demo 中的例子是展示工程中Draw ...

  5. Android实战简易教程-第九枪(BitmapFactory.Options对资源图片进行缩放)

    我们知道,我们编写的应用程序都是有一定内存限制的.程序占用了过高的内存就easy出现OOM(OutOfMemory)异常.因此在展示高分辨率图片的时候,最好先将图片进行压缩,压缩后的图片大小应该和用来 ...

  6. Android资源(图片)命名规范

    (转自:http://www.jb51.net/article/38796.htm) 图片命名注意: 1,不能以下划线("_")开头: 2,以数字加下划线("[0-9]_ ...

  7. extension 的一个应用 - 优化图片的读取机制

    枚举和 extension 都是 swift 中非常好用的特性.这里我们就来讨论一个应用的例子,供大家参考. 我们在开发 app 的时候,都会用到各种图片资源,而我们读取图片资源时主要是通过UIIma ...

  8. Android 图片Bitmap,drawable,res资源图片之间转换

    一.知识介绍 ①res资源图片是放在项目res文件下的资源图片 ②BitMap位图,一般文件后缀为BMP,需要编码器编码,如RGB565,RGB8888等.一种逐像素的显示对象,其执行效率高,但缺点也 ...

  9. 在Android开发中替换资源图片不起作用的解决方法

    现象 在android开发中,经常会需要替换res\drawable中的图片,打开res\layout下的文件预览布局页面发现图片已经被替换,但在模拟器或者真实机器上运行时发现该图片并没有被替换,还是 ...

随机推荐

  1. ShiroFilterFactoryBean分析

    创建核心Filter 同其他框架一样,都有个切入点,这个核心Filter就是拦截所有请求的. 通过web.xml中配置的Filer进入,执行init方法获取这个instance,调用下面的create ...

  2. JZYZOJ 1382 光棍组织 状压dp

    http://172.20.6.3/Problem_Show.asp?id=1382   水得过分了,本来以为要用lzx学长的写法写,抱着试试看的想法写了个特暴力的dp+dfs,过了,真是...   ...

  3. 【动态规划】【二分】Petrozavodsk Winter Training Camp 2017 Day 1: Jagiellonian U Contest, Monday, January 30, 2017 Problem B. Dissertation

    题意: 给定S1串,长度100w,S2串,长度1k.问它俩的LCS. f(i,j)表示S2串前i个字符,LCS为j时,最少需要的S1串的前缀长度.转移的时候,枚举下一个字符在S1的位置即可.(可以预处 ...

  4. 【模拟】【set】hdu 4789 ICPC Ranking

    写了一晚上,TLE到死,我选择GG 喵的好像还是前几年我校出的题,这整场都……tm…… 改日在战 /*TLE代码*/ #include<cstdio> #include<vector ...

  5. mysql表相关操作

    表的完整性约束 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性 主要分为: not null  标识该字段不能为空 default   为该字段设置默认值 unsign ...

  6. BigInt的实现——C++编程风格读书笔记

    C++编程风格这本书前面一些章节都觉得很简明易懂,但是读到效率这一章是才充分认识到读别人的代码还是很痛苦的一件事.书中给出的需要改进的初始类如下: class BigInt { private: ch ...

  7. Codeforces Round #345 (Div. 2) E. Table Compression 并查集

    E. Table Compression 题目连接: http://www.codeforces.com/contest/651/problem/E Description Little Petya ...

  8. iOS笔记 基于MKNetworkKit的断点续传

    http://iiiyu.com/2012/07/07/learning-ios-notes-eight/

  9. [典型漏洞分享]上传导致的应用层DOS攻击

    YS 视频封面设置功能可上传大量图片,可进行资源消耗型DOS攻击[中] 问题描述:          YS允许用户为设备设置封面,后台在处理时允许用户间接可控上传图片的二级路径以及直接可控保存图片的文 ...

  10. Android Logcat Security(转)

    讲解了在Android开发中logcat使用不当导致的安全问题 原帖地址:http://drops.wooyun.org/tips/3812 0x00 科普 development version : ...