Android资源图片读取机制
转自:http://blog.csdn.net/yellowcath/article/details/39641445
在新建一个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资源图片读取机制相关推荐
- android 资源图片加密
android 中有些重要的图片资源可以选择加密,因为不管是加固还是混淆,资源文件的图片始终能被看到, 加密图片步骤 1. 创建java项目安作为加密图片的工具,新建一个project,在main ...
- xamarin.android 资源图片问题
在xamarin.android 中,关于图片的资源一般都在Resources.drawable下面,在Resources这个文件夹下面,包含了drawable.drawale-hdpi.drawab ...
- Android 放图片背景的时钟
自定义View 时钟:可以用画布和画笔直接画时钟画刻度,画指针,但是,效果不是很好看,这篇文章主要介绍,两种采用背景图设置的时钟,包括表盘,指针,都是图片. 效果图: ...
- Android Glide图片加载框架(三)缓存机制
文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...
- Android之对资源图片进行比例缩放
效果图: 在平时加载图片时,我会使用SetImageBitmap.setImageResource.BitmapFactory.decodeResource来设置一张图 片通过以上方法来设置图片时,会 ...
- Android手机内存图片读取,有效解决Android加载大图片内存溢出的问题
今天在交流群里,有人问我他经常遇到加载图片时内存溢出的问题,遇到的情况还是在自己的测试机或者手机里没有问题,做好了, 到了客户手机里就内存溢出了.其实有时候不同的手机和不同的系统对内存的要求不一样,尤 ...
- android python 纠正图片,Python脚本替换Android资源(包名,图片,文件内容)
最近要将Android项目中的gradle,图片,包名,字符串等做便捷替换,以适应不同内容的更换,于是搬出半生不熟的Python,通过一系列的文件操作达到目的.完整项目写在github上,欢迎fork ...
- Android中ImageSwitcher结合Gallery展示SD卡中的资源图片
本文主要是写关于ImageSwitcher结合Gallery组件如何展示SDCard中的资源图片,相信大家都看过API Demo 中也有关于这个例子的,但API Demo 中的例子是展示工程中Draw ...
- Android之设置资源图片为圆角图片
效果图: 参看以下代码: public class MainActivity extends Activity {private ImageView imageView1;private ImageV ...
最新文章
- 基于OpenCV的显著图绘制
- 系列文章|OKR与敏捷(二):实现全栈敏捷
- java 编码二进制写法、十六进制用源代码表示
- 露雨资源库(第一个.net2.0软件)二
- 公司的一道考试题算法分析——大数据量整数排序
- 数据结构入门学习笔记-1
- 事务管理:事务的状态相关知识笔记
- Python二级笔记(1)
- 深入理解java虚拟机 (三) 第二版
- SystemTimer,TimerTaskList等源码分析
- 算法导论( FFT 自动机 最优二叉搜索树 !!!)
- 美团架构师熬夜整理:Netty权威指南2.0版+英雄传说项目
- Linux服务器使用网络代理
- 连续自然数之和 C++
- SPEA2算法原理及应用方向
- 解决-iOS: Thead 1: signal SIGABRT
- manjaro折腾手记
- whm面板降mysql_WHMCS与Cpanel/WHM面板整合方法-Cpanel/WHM管理使用教程 | 麦田一棵葱...
- Spartan6系列之时钟资源详解
- 虚拟机ubuntu主机板子三者ping通
热门文章
- 谈谈赚钱的套路 - 敏锐,决断及行动
- 惩罚孩子的10大智慧 [转]
- 带着问题分析Framework层源码(一):按键音声音太小,我们该如何增大?
- 代码编辑器Sublime_Text3的使用
- 窃取QQ中社会工程学的应用实例
- c++ opencv数字图像处理:频率域滤波--高通滤波--高斯高通滤波
- 百趣代谢组学分享Lip-SMap:绘制代谢物和蛋白相互作用图谱的新方法
- 基于Java毕业设计病人跟踪治疗信息管理系统演示2021源码+系统+mysql+lw文档+部署软件
- fileview启动office组件失败
- 通过 Python 查询 Excel 数据