在新建一个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()

ssize_t ResTable::getEntry(
const Package* package, int typeIndex, int entryIndex,
const ResTable_config* config,
const ResTable_type** outType, const ResTable_entry** outEntry,
const Type** outTypeClass) const
{
********省略******* const size_t NT = allTypes->configs.size();
for (size_t i=0; i<NT; i++) {
const ResTable_type* const thisType = allTypes->configs[i];
if (thisType == NULL) continue; ResTable_config thisConfig;
thisConfig.copyFromDtoH(thisType->config); ********省略******* if (type != NULL) {
// Check if this one is less specific than the last found. If so,
// we will skip it. We checkstarting with things we most care
// about to those we least care about.
if(!thisConfig.isBetterThan(bestConfig, config)) { //就是这里
TABLE_GETENTRY(ALOGI("Thisconfig is worse than last!\n"));
continue;
}
} type = thisType;
offset = thisOffset;
bestConfig = thisConfig;
TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
if (!config) break;
}
********省略*******
return offset + dtohs(entry->size);
}

ResTable_config::isBetterThan()

bool ResTable_config::isBetterThan(const ResTable_config& o,

        const ResTable_config* requested) const {

    if (requested) {

          **************

        if (screenType || o.screenType) {

            if (density != o.density) {

                // density is tough.  Any density is potentially useful

                // because the system will scale it.  Scaling down

                // is generally better than scaling up.

                // Default density counts as 160dpi (the system default)

                // TODO - remove 160 constants

                int h = (density?density:160);

                int l = (o.density?o.density:160);

                bool bImBigger = true;

                if (l > h) {

                    int t = h;

                    h = l;

                    l = t; 

                    bImBigger = false;

                }

                int reqValue = (requested->density?requested->density:160);

                if (reqValue >= h) {

                    // requested value higher than both l and h, give h

                    return bImBigger;

                }

                if (l >= reqValue) {

                    // requested value lower than both l and h, give l

                    return !bImBigger;

                }

                // saying that scaling down is 2x better than up

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

                    return !bImBigger;

                } else {

                    return bImBigger;

                }

            }

            ***********
} } return isMoreSpecificThan(o); }

关键部分已用红字标明,在多个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:

    enum {
DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
DENSITY_LOW =ACONFIGURATION_DENSITY_LOW,
DENSITY_MEDIUM =ACONFIGURATION_DENSITY_MEDIUM,
DENSITY_TV = ACONFIGURATION_DENSITY_TV,
DENSITY_HIGH =ACONFIGURATION_DENSITY_HIGH,
DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH,
DENSITY_XXHIGH =ACONFIGURATION_DENSITY_XXHIGH,
DENSITY_XXXHIGH =ACONFIGURATION_DENSITY_XXXHIGH,
DENSITY_NONE =ACONFIGURATION_DENSITY_NONE
};

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

    ACONFIGURATION_DENSITY_DEFAULT = 0,
ACONFIGURATION_DENSITY_LOW = 120,
ACONFIGURATION_DENSITY_MEDIUM = 160,
ACONFIGURATION_DENSITY_TV = 213,
ACONFIGURATION_DENSITY_HIGH = 240,
ACONFIGURATION_DENSITY_XHIGH = 320,
ACONFIGURATION_DENSITY_XXHIGH = 480,
ACONFIGURATION_DENSITY_XXXHIGH = 640,
ACONFIGURATION_DENSITY_NONE = 0xffff,

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

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

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

TypedValue typedValue = new TypedValue();
getResources().getValue(R.drawable.test,typedValue,true);
//然后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. Abp数据库迁移注意事项

    前记:昨天下载了一个Abp模板,然后尝试利用EF CodeFirst进行数据库生成操作,然后就是一直报错 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务 ...

  2. Xamarin.Forms教程下载安装Xamarin.iOS

    Xamarin.Forms教程下载安装Xamarin.iOS 下载安装Xamarin.iOS Xamarin.iOS可以为Mac上iOS应用程序在Windows计算机上编写和测试网络提供构建和部署服务 ...

  3. ElasticSearch Mapping中的字段类型

    1)string: 默认会被分词 2)数字类型主要如下几种: long:64位存储  integer:32位存储  short:16位存储  byte:8位存储  double:64位双精度存储  f ...

  4. VB查询数据库之终极篇——机房收费系统总结(七)

    机房收费系统总结系列到此就算是要结束了.在做机房收费系统之前,做了学生管理系统,可以说,对机房收费系统的帮助很大. 在机房收费系统中,数据库的查询,数据写入数据库,更改数据库内的信息等一些操作在学生信 ...

  5. POJ3480 John 博弈论 anti-nim anti-SG

    http://poj.org/problem?id=3480 anti-nim其实是anti-SG的一种,就像nim是sg的一种一样.(或者说sg是nim推广?) 看名字就是规则和nim相反,取到最后 ...

  6. android:scrollbarStyle属性及滚动条和分割线覆盖问题

    android:scrollbarStyle可以定义滚动条的样式和位置,可选值有insideOverlay.insideInset.outsideOverlay.outsideInset四种. 其中i ...

  7. 随机数选择器 Exercise07_13

    import java.util.Scanner; /** * @author 冰樱梦 *时间:2018年下半年 *题目:随机数选择器 */ public class Exercise07_13 { ...

  8. idea小问题解决方法系列

    1)有些java文件上显示“红色小j”,如图所示 ,意思是“不可编译”,原因是Module未导入或者没有标记Module下"Sources"一栏src文件夹为Sources.(ht ...

  9. php读取超大文件fseek

    function readMaxFile($fp , $start = 0) { $tag = "\n"; $i = 0; $content = ''; while($i < ...

  10. 一个简单的MVC模式练习

    控制层Action接受从模型层DAO传来的数据,显现在视图层上. package Action; import java.sql.Connection; import java.sql.SQLExce ...