在新建一个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. ARM开发板不工作的几个原因

    刚焊了5块ARM(LPC2478)的开发板,上程序测试了一下,发现只有一个板子工作其他四个全部歇菜.努力地找了一会最终发现是板子的来个电阻焊翻了.因为是1206 的封装而且来个电阻在PCB上摆放的位置 ...

  2. You are my brother NBUT - 1218

    问题描述 Little A gets to know a new friend, Little B, recently. One day, they realize that they are fam ...

  3. Redux 洋葱模型理解

    下面的代码会输出: A middleware1 开始C middleware2 开始E middleware3 开始======= G =======F middleware3 结束D middlew ...

  4. HTML 钟表 小时钟

    该放假了,心情不好,写个小表针感慨一下时间为什么过得如此之快,写了个小钟表. 提示 1:这个钟表的秒针转的非常快,如果需要和当前的网络时间一样,请修改</script>上一行的代码,把1换 ...

  5. Linux下Git命令中文显示乱码的问题解决:274\232\350\256\256\346\200\273\347\273\223

    使用git add添加要提交的文件的时候,如果文件名是中文,会显示形如274\232\350\256\256\346\200\273\347\273\223的乱码. 解决方案:在bash提示符下输入: ...

  6. HDU 4639 Hehe (2013多校4 1008 水题)

    Hehe Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  7. 利用json2html将json数据填充到html模板

    1.下载json2html>> 2.制作好模板.准备好json数据.启动 <!DOCTYPE html> <html> <head> <meta ...

  8. 分公司下拉框赋值-从后台传到前端jsp

    我的旧代码  List<MetaBranchCfg> list = metaBranchCfgBO.queryAllBranchList();  request.setAttribute( ...

  9. 解决kylin报错 ClassCastException org.apache.hadoop.hive.ql.exec.ConditionalTask cannot be cast to org.apache.hadoop.hive.ql.exec.mr.MapRedTask

    方法:去掉参数SET hive.auto.convert.join=true; 从配置文件$KYLIN_HOME/conf/kylin_hive_conf.xml删掉 或 kylin-gui的cube ...

  10. leetcoder-50-Pow(x, n)

    Pow(x, n) 能够直接用库函数pow(x,n)一步搞定,但明显这样就没意思了.   參考   快 速 幂 取 模 二分.复杂度为O(logn) 递归方法 class Solution { pub ...