Android 基于dpi的资源加载

原创 2013年09月23日 11:52:09

    Android设备碎片化问题一直被开发者广为诟病,而且,因为目前手机屏幕越来越来,分辨率越来越高,大有愈演愈烈的趋势。除了等待Google给出一个更加有效的解决方案以外,我们只能尽量适应当前的环境,尽量让自己的产品能够在更多的产品上正常运行。


    在Google的建议下,开发者普遍使用dpi/dp单位,进行UI设计。本文将会介绍dalvik基于dpi加载资源的规则。


DPI

    DPI,全称 dots per inch,意为每英寸的直线上像素点的数量。dpi越高,屏幕的画面越清晰,画质越细腻。

    目前的Android设备支持以下几种DPI:

类别 dpi 缩放比率 代表机型
LDPI 120 75% 已经被市场淘汰
MDPI 160 100% 基本被市场淘汰
TVDPI 213 133% Google Nexus 7
HDPI 240 150% Sumsong Glaxy S2, Google Nexus S, MOTO Droid
XHDPI 320 200% Sumsong Glaxy S3, Sumsong Note2, Google Nexus 4
XXHDPI 480 300% 目前市场上各品牌的旗舰机:Sumsong Glaxy S4、 Asus Padfone Infinite、HTC One、 小米3

    上面表格中dpi又称归一化DPI,可以算是一种“理想化”的DPI标准。

    以Eclipse新建Android项目默认提供的机器人ICON为例:

    在drwable-hdpi的文件夹中,ic_launcher.png的size为72*72,而drawable-xhdpi文件夹中的ic_launcher.png的size为96*96。

    理论上来说,在Sumsong Note2(5.5寸,1280*700,XHDPI)上,会加载drawable-xhdpi中的ic_launcher.png,96/320=0.3,使用者会看到一个0.3*0.3英寸的机器人ICON,而在Sumsong的Glaxy S2上(4.3寸,480*800,HDPI)上,会加载drawable-hdpi中到ic_launcher.png,72/240=0.3,使用者仍旧会看到一个0.3*0.3英寸的机器人ICON。所以,从结果上来说,使用者在不同的设备上得到了相同到UI效果,而且,因为在Note2上使用了96*96的ICON, 可以获得更加精细的画面,这似乎是个很理想到结果:即维持了体验的一致性,又最大化的利用了屏幕的显示效果。

    但是,理论和实际总是会有些微妙的差别。设备制造厂商为了迎合消费者的喜好,会生产各种屏幕尺寸的设备,而Android的归一化DPI只有6种(其中2种还是被市场淘汰的),最终呈现给开发者的结果就是,硬件设备的物理dpi(或者说ppi,pixel per inch)总是或大或小,和归一化dpi有一定差距,厂商会根据自己的需要设定设备的归一化DPI,而dalvik进而根据这个归一化DPI来加载资源,绘制界面。

    Note2的物理分辨率其实是267ppi(其实,更接近HDPI,而非XHDPI),而非dalvik认为的320,96/267=0.36,所以,使用者实际看到的是一个0.36*0.36的ICON,而S2的物理分辨率是219,72/219=0.33,所以使用者实际看到的ICON为0.33*0.33寸。所以,界面的实际效果会和开发者的预想有一定偏差。

    幸运的是,只要设备制造商设定的归一化DPI和设备的物理分辨率差距不会大的离谱(想象一下,DPI设置不佳导致大部分app都无法正常运行的设备,能够大卖么?),界面的最终效果还是能够达到开发者的要求的。


基于DPI的资源加载优先级

    首先,我们需要明白dalvik匹配最佳资源的策略,从Google的官方资料,我们可以知道dalvik是这样工作的:

  1. 根据设备的属性,排除所有存在冲突属性的资源,注意两点:
    • 不对Screen pixel density & screen size两个属性做检查
    • 如果在这一步就排除了所有资源,则抛出ResourceNotFoundException
  2. 挑选当前优先级最高的属性
    • 属性的优先级排序为:MCC and MNC、 Language and region、 Layout Direction、 smallestWidth、 Aailable width、 Aailable height、 Screen size、 Screen aspect、 Screen orientation、 UI mode、 Night Mode、 Screen pixel density(dpi)、 Touchscreen type、 Keyboard availability、 Primary test input method、 Navigation key availability、 Primary non-touch navigation method、 Platform Version
  3. 如果存在包含了当前属性的资源,则执行4,否则跳过当前属性,执行2
  4. 排除所有不包含该属性的资源
    • 如果当前属性为screnn pixel density,则以如下方式进行匹配:
      • 如果有最匹配的资源(e.g. 设备是HDPI,存在hdpi的资源),则删除其他的资源
      • 如果没有最佳匹配资源,优先匹配更高dpi的资源,缩小合适的比例以后使用(e.g. 设备是HDPI,未能找到hdpi的资源,但是有xhdpi的资源,则把XHDPI的资源缩小的3/4以后使用),并排除其他的资源(Google解释说,因为执行缩小操作比执行放大操作更加方便,所以高dpi资源优先与低dpi资源,不过,个人认为对于大部分图片来说,大图缩小造成的失真应该是小于小图放大造成的失真)
      • 如果没最佳匹配的资源,也不存在更高dpi的资源,则使用dpi更低的资源,并放大合适的比例以后使用(e.g. 设备为HDPI,未能找到hdpi以及更高的资源,单存在mdpi的资源,则把mdpi的资源放大到3/2以后使用),并删排除其他资源
    • 如果当前性为screen size,则以如下方式进行匹配:
      • 如果有最佳匹配资源(e.g. 设备为large,存在large的资源),则排除其他资源
      • 如果没有最佳匹配资源,则使用更小size的资源(e.g. 设备为large,但是存在normal资源,则使用normal资源),排除其他资源
      • 如果没有最佳匹配资源,也没有更小size的资源,则抛出ResourceNotFoundException
  5. 是否仅剩余唯一的资源,是则执行6,否则执行2
  6. 以唯一的资源为最佳资源,匹配结束

    从Google的资料中,我们可以知道dalvik匹配最佳资源的逻辑,但是还有四个问题未解释清楚:

  1. 当最佳dpi资源不存在,但是高dpi资源大于一个时(e.g. 设备为hdpi,但是apk仅提供xhdpi & xxhdpi资源),如何选择资源?
  2. 当最佳dpi资源&高dpi资源都不存在,但是低dpi资源大于1个时(e.g. 设备为hdpi,但是apk仅提供mdpi & ldpi资源),如何选择资源?
  3. 当drawable资源不包含screen pixel density(e.g. 文件夹名为”drawable“,下文中称之为default)属性时,如何选择资源?
  4. 当资源被nodpi修饰时,如何选择资源?

    既然没有现成的资料,就让我们自己测试:

测试设备:Nexus 7(TVDPI)

测试代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

	<ImageView
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    android:src="@drawable/rabit"
	    android:scaleType="center"
	    android:contentDescription="@string/content_description_of_image"
	    />

</RelativeLayout>
    仅在Layout根目录添加了一个ImageView,未修改Eclipse自动生成的java代码。

测试图片:rabit.png, size:600*450pixel

问题1:

仅提供XHDPI & XXHDPI两种资源:


运行结果:


从执行结果,我们可以看到,dalvik使用了xhdpi的资源,并且进行了合适的缩小:600*1.33/2=399,450*1.33/2=299.

结论:当最佳dpi资源不存在,而高dpi资源大于1个时,选择更接近设备的资源(即较低dpi的资源),并根据缩放比缩小合适的比例后使用。

问题2:

仅提供ldpi和mdpi两种资源:


运行结果:


从运行结果,我们可以知道davlik加载了mdpi,并放大了合适的倍数:450*1.33/1 = 599(因为这里图片水平方向上已经被截断少许,所以未测量)。

结论:当最佳dpi资源&高dpi资源都不存在,而且dpi资源大于1个时,选择更接近设备的低dpi资源,并根据缩放比放大合适的比例后使用。

问题三:

第一步测试:

仅提供mdpi,ldpi,default三种资源:


运行结果:


从运行结果来看,mdpi资源的优先级要高于default。

第二步测试:

在第一步的基础上,删除mdpi下的rabit.png:


运行结果:


从运行结果看,default的优先级高于ldpi

另外,我们可以看到default的缩放比和mdpi是一致的,由此我猜测,因为Android是以mdpi作为dpi基准,所以,default等同与mdpi,但是,资源优先级而言mdpi高于default。根据这个猜测,对于ldpi的设备来说,优先级顺序会是这样的:ldpi>mdpi>default>hdpi,不过找不到ldpi的设备,也无法验证。

结论:对于高于hdpi(包括hdpi)的设备来说,default资源的优先级高于ldpi但是低于mdpi,并且缩放比等于mdpi

问题4:

步骤1,仅提供ldpi和nodpi的资源:


运行结果:


从运行结果来看,ldpi的优先级高于nodpi。

步骤2,在步骤1的基础上删除ldpi的资源:


运行结果:


从运行结果,我们看到,图片的确如google官方资料所说,未经过任何缩放。不过,google提供nodpi似乎是非常的不愿意啊,优先级最低,仅在无图可用的情况下,才会使用nodpi的资源。


结论

原则上来说,dalvik优先使用符合设备dpi的资源,其次是dpi较低的高dpi资源,再次是dpi较高的高dpi资源,最后采用nodpi的资源,由此,根据设备自身的dpi的不同,不同dpi资源的优先级是有差异的(忽略mdpi&hdpi):

设备dpi 优先级顺序(由高到低)
tvdpi tvdpi>hdpi>xhdpi>xxhdpi>mdpi>default>ldpi>nodpi
hdpi hdpi>xhdpi>xxhdpi>tvdpi>mdpi>default>ldpi>nodpi
xhdpi xhdpi>xxhdpi>hdpi>tvdpi>mdpi>default>ldpi>nodpi
xxhdpi xxhdpi>xhdpi>hdpi>tvdpi>mdpi>default>ldpi>nodpi
另外,除了nodpi以外,使用其他dpi资源前,还需要根据缩放比进行缩小/放大操作。

    经过同事的测试,上面的结论有一点小问题:

    hdpi的设备对于dpi的优先级顺序实际上是这样的: hdpi>tvdpi>xhdpi>xxhdpi>>mdpi>default>ldpi>nodpi(可能Google觉得tvdpi(213)还是很接近hdpi(240)的,可堪一用)


参考资料:

http://developer.android.com/guide/topics/resources/providing-resources.html

http://developer.android.com/guide/practices/screens_support.html

http://ivan-ru.iteye.com/blog/1711414

相关文章推荐

多屏幕多分辨率的支持和一些概念

快速预览 Android 在不同的屏幕大小和密度上执行屏幕的显示会影响用户界面的效果系统处理大多数的自适应屏幕你应该针对屏幕创建资源以更好的控制你的UI   Android 是一个系统,运行...
  • vrix
  • vrix
  • 2012年06月01日 16:18
  • 8119

获取手机屏幕密度dpi和对应的资源文件夹

1、首先看一下像素密度对应的Android资源文件夹 dpi范围 密度 0dpi ~ 120dpi ldpi 120dpi ~ 160dpi mdpi 160dpi ~ 240dp...

理解Android中dpi和分辨率的关系,谈谈Android做成适应全部手机的UI方式

最近我在做一个界面,UI给的切图是1280x720这个分辨率的,给的标注单位是px(像素)。 我把原图自然而然地放在drawable-xhdpi中,然后开始调整布局(当时我默认1dp=2px)...

Android屏幕适配(三)适配文件的自动生成和系统适配

前面说了 dp、dpi、px、sp 的概念和计算,不同密度对应的分辨率,适配的方法。 怎么样去生成vaues文件夹下面的文件呢? 如果每个dpi文件夹都去手动写dimens.xml是一件很...

Android适配——采用Values-dpi-wSize X hSize 模式,并分析原理

这篇文章主要讲两点: 一、xxxhdpi、560dpi、xxhdpi、xhdpi、hdpi、mdpi、ldpi (还有tvdpi 主要用于电视,不去讨论) 在某个dpi缺失的情况下,如果去找寻。 二、...

android:为TextView添加样式——下划线,颜色,设置链接样式及前背景色

实现下划线及颜色设置: public class AtActivity extends Activity { LinearLayout ll;     /** Called wh...

Android加载不同DPI资源与内存消耗之间的关系

2015/04/21 目录 [隐藏] 1 Android DPI 分级标准简介 2 不同设备共享同一套 DPI 资源有哪些问题? 3 问题:DPI 越小的文件夹内存...
  • L25000
  • L25000
  • 2015年04月30日 12:31
  • 352

[授权发表]Android加载不同DPI资源与内存消耗之间的关系

DPI (Dots per inch) 是屏幕像素密度的衡量标准,Android为支持不同物理尺寸的屏幕,允许APP针对不同DPI配置不同资源,也允许“偷懒”只配置一种,让Android 自动拉伸/缩...

Android 加载不同 DPI 资源与内存消耗间的关系

1 Android DPI 分级标准简介 Android 设备在物理尺寸和屏幕密度上都有很大的不同,为了简化多设备的设计方案,就是设定一套分级标准。屏幕密度上的分级标准就是:LDPI、MDPI、...

pad 强制加载 Hdpi资源 (2.3 dpi < 240)

做一款pad产品,屏幕是7寸,分辨为1024*600。芯片厂商给的代码运行起来但视觉效果很差,第一眼就可以看出壁纸被严重拉伸过,图片色块和锯齿很明显,一开始怀疑是屏驱动没有调好,找专人对lcd驱动进行...
  • aaa2832
  • aaa2832
  • 2012年05月17日 17:54
  • 1541
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 基于dpi的资源加载
举报原因:
原因补充:

(最多只允许输入30个字)