第一部分:开发前的准备-第四章 多分辨率多屏幕的支持

第4章 多屏幕多分辨率的支持

如果你买过Android手机的话,销售员一般会向你推荐这个android手机是最新版的系统啦,屏幕有多大,分辨率是多少之类的。这只是外表上的多屏幕和多分辨率,其实android内在还有一些影响因素,例如“密度”的概念。本章为详细的为你解释一些基础概念并适当的举一些例子教你怎么处理关于多屏幕多分辨率的支持。 虽然Android系统内置有一些机制能使用多屏幕多分辨率,但我们还是应该在加点外力来更加优化用户体验的效果。

4.1多屏幕支持概述

4.1.1基本术语和概念

屏幕大小

根据屏幕对角线实际测量的物理大小(android已经根据屏幕大小分为四类small, normal, large, extra large)

屏幕密度

屏幕物理区域一个单位(一般用英寸)的像素数量,一般简称dpi(‘dots per inch’每英寸中包含的点的数量,记住DPI这个概念很重要)

这里android也分为四类 low, medium, high, extra high.

方向

从用户的角度看设备只有两个方向,那就是横屏和竖屏。关于横竖屏最好也要出一套相对应的UI,因为长宽比不一样,特别是对于软件项目一般都是出两套(游戏一般都会锁定一个方向)。

分辨率

一个物理屏幕上像素点的总数。一般我们的应用不会直接使用分辨率,而是使用密度和屏幕大小(但某些特殊情况下,可以指定分辨率)

独立密度像素(Density-independent pixel ‘dip’)

一个虚拟的单位,用来定义UI布局的。它是表述布局的尺寸大小或者位置。

dip(注 意dip是独立像素单位,dpi是屏幕密度)等价于一个160dpi(medium密度)的屏幕,运行时系统会根据你实际屏幕的密度来自动处理缩放。像素和dip 的转换公式为px =dp*(dpi/160) 。例如在240密度的屏幕上一个dp等于1.5个像素(dp=px*1.5)。以后我们应该尽量使用dip单位布局,不要使用像素单位。这样会使你的应用屏幕兼容性更好。最后我们应该区分dpi和dip它们一个字母的不同代表的意思就不一样。以后程序中我们一般使用dp来代替dip用来描述位置和尺寸大小,而dpi则是屏幕密度,这里大家需要稍微留心一下。下面我们来举个游戏的例子:

关于android多屏幕多分辨率这道门槛,我们可以把它看成一个boss,要战胜这个boss我们需要了解它的5个弱点(也可以理解成游戏套路)

  1. 屏幕大小---boss的体型,boss都有体型之分的,例如WOW中的BOSS大部分都是巨型,而有些游戏中的boss比较小,例如剑网3的boss就比较小。
  2. 屏幕密度---boss的模型细粒度,有些boss你能看清它的每个外观细节,但有些boss就比较模糊,虽然你的电脑显卡或者游戏模型的材质会影响外观细节。但请忽略它们!
  3. 方向---这个要以我们的角度来观察boss才能得出结论,例如你靠近boss并站在高处和你远离boss站在低处看boss完全是两个感觉。
  4. 分辨率---这个可以理解为对boss总体的外观和风格,它是由boss的体型和boss的模型细粒度加在一起体现出的一种外观和风格,所以说分辨率我们一般是通过密度和屏幕大小来匹配。
  5. dip---boss的伤害公式,boss的伤害其实是一个数字,但这个数字是有公式转换的,例如法系攻击,物理攻击的都是有一个公式转换后在附加到我们身上。当然请不要纠结你的防御,抗性之类的,dip就是一种转换公式,让不同的boss都能统一转换成正确的伤害。

4.1.2多屏幕支持的范围

Android1.6开始就支持多屏幕和密度了,这都是为了更好的用户体验,为了简化我们多屏幕的开发。android提供了一组范围让我们使用:

关于屏幕大小: small, normal, large, xlarge(看来目前游戏boss的体型就这四种)

注意: android3.2会有些不同,以后我们在讲平板和手机支持时会说明

屏幕密度: ldpi (low), mdpi (medium), hdpi (high), xhdpi (extra high) (boss的模型细粒度目前也就四种了)。这些广义的大小和密度都是基于normal大小和mdpi来当做标准的。一般来说我们分辨率为800*480或者854*480的手机密度都是240(hdpi),屏幕大小都为large。

每一个大小和密度都有一个跨越幅度。但这些幅度中具体的微调我们是不用关心的,android会帮我们处理,下面我们来看图 4-1:

 

 图4-1 实际大小和实际密度的取值范围(这是个近似值)

 

我们设计UI都会要考虑一个最小化的空间,android中都是使用dp(dip)单位的。以下是官方参考

xlarge 屏幕至少960dp x 720dp

large 屏幕至少 640dp x 480dp

normal 屏幕至少 470dp x 320dp

small 屏幕至少 426dp x 320dp

屏幕大小和密度是可以独立的,不是说一个5寸屏的密度就一定是hdpi。有一个很重要的例子就是例如一个WVGA (800×480)屏幕可能在不同的设备上会有mdpi和hdpid两种密度。也就是说同样体型的boss,有的模型很粗糙,有的很细腻。

 

4.1.3独立密度

独立密度是很重要的,因为没有它,一个UI元素(比如一个按钮)会出现在一个物理屏幕很大,但密度很少的显示效果(看起来就是像是被强行放大的,失真很严重)。这样相对位置的改变会出问题,下面我们来看图:

 

 图4-2 你的应用没有支持不同密度产生的效果

 

图4-3你的应用具有良好的支持而产生的效果

android 系统帮助你的应用实现独立密度的方法有两种:

1.系统缩放dp单位来适用当前屏幕密度

2.系统基于当前屏幕密度缩放drawable资源到适用的大小

图 4-2是用像素单位来定义的大小。你发现他们的布局完全变了,有的大有的小。这是因为他们的实际屏幕大小可能是一样的,高密度屏幕每英寸的像素更多(所以你会发现像素一样,但高密度的设备显示出来的效果却很小)。图4-2使用的是dp单位,基于mdpi密度的设备它不会变化,ldpi它会自动缩小,hdpi它会自动放大。所以效果非常好。图片显示出来的样子和大小完全一样。

使用密度系统会缩放图片使之看上去完全一样,但细心的你会发现有些图片变模糊了。所以我们应该提供可选择的图片资源让系统来自动选择图片以适应不同的密度。所以用Eclipse自动生成的工程项目中的hdpi,mdpi,ldpi三个文件夹就是用在这里的。这样就不会有模糊的感觉了。当然对于矢量图来说放大的失真也没什么影响,这里可以理解为只针对位图。

4.2怎样支持多屏幕

以上我们学习了这么多的理论基础。现在我们应该优雅的处理它们了。

1. 明确的在manifest文件中指出你的应用支持的屏幕大小。

我们可以有选择性的过滤掉一些老掉牙的设备,以减轻我们的工作量。但这并不是一个好的方法仅供参考。在manifest 中使用节点<supports-screens>

2. 根据不同的屏幕大小提供不同的布局

默认的android会调整你应用的布局来适应当前设备的屏幕。大多数情况下可以满足,但某些特殊情况,你的UI可能需要一个更精确的调整。例如,一个larger 屏幕,你可能想要利用额外的屏幕空间调整位置和一些元素的大小,或者一个小屏幕中,你可能需要调整到刚好合适。比如你需要适应一个extra large 屏幕。你可以在文件夹layout-xlarge/下放置布局(读者请记住一般layut下放的都是xml布局文件)。

Android 3.2开始,有一种新的方式。例如你的平板需求至少600dp的屏幕宽度,你可以使用layout-sw600dp 这种文件夹放置你的布局文件。以后我们在平板一节会详细描述。

3. 根据不同的屏幕密度提供不同的位图

默认android会缩放你的位图(.png.jpg.gif),另外还有一种专用的Nine-Patch(.9.png)。例如,你的应用提供的位图仅仅为标准密度(mdpi),那么在hdpi的屏幕上这些位置会被等比放大。所以我们应该有选择性的对于不同的屏幕密度包含不同的分辨率。

所以android针对图片资源配置后缀,前面我们知道的4种密度(ldpi,mdpi,hdpi,xhpi),如果用于高密度的屏幕,我们应该使用drawable- hdpi/这种文件夹。以上关于屏幕大小和密度的后缀都符合屏幕支持的那些范围。

在运行时, 对于给定的资源系统会使用以下这些步骤来显示:

1. 系统会使用合适的替代资源

基于当前屏幕的大小和密度,系统会针对屏幕的密度和大小使用资源。例如,如果设备有一个 hdpi的屏幕那么当App请求一个drawable 资源时,系统会在最匹配的的设备配置下的drawable 资源目录寻找。可以取决于其他可替代的资源,使用drawable-hdpi/的资源目录最匹配的,所以系统会使用drawable-hdpi目录下的资源。

2. 如果没有可用的匹配资源,系统会使用默认资源等比放大或缩小以匹配当前屏幕的大小和密度。

这里的默认资源是没有后缀的。例如,“drawable”就是默认的drawable/ 资源。系统会认为默认的资源都是基于normal 屏幕大小和medium 密度设计的。系统会等比缩放默认的密度资源来匹配hdpi密度或者ldpi密度。当系统没有找到针对密度资源的目录时,它就一直会使用默认的资源。例如,如果系统想找一个ldpi(低密度)的资源,但找不到。那么系统会等比缩小hdpi的资源,为什么不寻找mdpi呢?因为系统对于hdpi更容易缩放,它的系数为0.5,相比mdpi的0.75来说。0.5的的性价比更高(方便计算,正确率也高一点)。关于这一点最好的证明就是android版本的QQ浏览器。你解压后发现它关于drawable就只有drawable和drawable-hdpi两个文件夹,看来腾讯里android的开发者应该很熟悉android系统规律了,因为默认的drawable 就是mdpi,只需要定义一套hdpi的drawable资源就行了。

4.2.1使用配置后缀

Android支持几种配置后缀,它允许你控制系统有选择性的筛选基于当前屏幕设备的资源文件。一个配置后缀就是一个String你可以在资源目录文件夹下追加指定的后缀即可。使用一个配置后缀的方法步骤:

1.在你的工程res/目录下创建一个新的目录,格式为<资源名>-<后缀>: 

<资源名>是标准的资源名字(例如 drawable或者layout)

<后缀> 是指定屏幕配置使用哪一种资源(例如hdpi或者xlarge)

我们可以使用的后缀不仅仅只有一个哦,在一个文件夹下我们可以同时使用多种后缀来限定资源,使用"-"即可。

2.在新的目录中保存合适资源,资源文件的命名必须和默认资源文件一样。

例如 xlarge是一个后缀用于extra large 屏幕。当你追加后缀时(例如layout-xlarge) ,它说明系统会在extra large屏幕上使用这些资源

屏幕特征

后缀

描述

大小

small

资源用于small大小的屏幕.

normal

资源用于normal 大小的屏幕。(这是默认的基准大小)

large

资源用于large 大小的屏幕

xlarge

资源用于extra large 大小的屏幕

密度

ldpi

资源用于 low-density (ldpi) 密度的屏幕 (~120dpi).

mdpi

资源用于medium-density (mdpi) 密度的屏幕 (~160dpi).

(这是默认的基准密度.)

hdpi

资源用于high-density (hdpi) 密度的屏幕 (~240dpi).

xhdpi

资源用于extra high-density (xhdpi) 密度的屏幕 (~320dpi).

nodpi

资源用于所有密度. 系统不会根据当前屏幕密度去缩放资源

tvdpi

资源用于mdpi 和hdpi两者之间的某的密度;大约是213dpi。

这个很少用到。它主要用于电视,大多数App不需要用到它。

如果你需要tvdpi资源,它的大小大概是1.33*mdpi(160)例如,

一个在mdpi下100px*100px的图片,那么在 tvdpi中它会

变成133px*133px。的图片,那么在 tvdpi中它会变成133px*133px。

方向

land

资源用于横屏

port

资源用于竖屏

长宽比

long

资源用于长宽比相差很远的配置(相对于4寸(normal)屏幕左右基准屏幕)

notlong

资源用于长宽比差不多的配置(相对论,同上)

表格4-1 后缀配置表

注意: 如果对于android3.2或者更高的版本,有一些新的后缀,等下我们会讲解

下面我们就来举一些详细的例子:

res/layout/my_layout.xml             // 用于normal 屏幕大小的布局 ("默认")
res/layout-small/my_layout.xml       // 用于small 屏幕大小的布局
res/layout-large/my_layout.xml       // 用于large屏幕大小的布局
res/layout-xlarge/my_layout.xml      // 用于extra large屏幕大小的布局
res/layout-xlarge-land/my_layout.xml // 于extra large屏幕大小并且是横屏的布局

res/drawable-mdpi/my_icon.png        // 用于中等密度的位图
res/drawable-hdpi/my_icon.png        // 用于高密度的位图
res/drawable-xhdpi/my_icon.png       // 用于超高密度的位图

当android系统在运行时选择资源时,当你没有加后缀时它自己有一定的逻辑判断来匹配最适合的资源。当基于使用大小的后缀时,如果当前没有资源更好的匹配你的后缀,那么系统会使用比当前屏幕更小的资源来计算(例如,如果你没有添加large后缀,那么一个large大小的屏幕将会使用在normal(默认的)大小的屏幕资源来放大后显示),如果可用的资源后缀比当前屏幕更大(其实还是没匹配上),那么系统将不使用它们(例如你吧所有layout资源放入xlarge后缀中,但设备是一个normal屏幕,系统就不会使用这些资源)。

是不是有点头晕了,简要的概述下就是如果没有匹配的系统会使用默认的资源,如果连默认下都没有放资源并且没有匹配到你的后缀,那么系统就没有资源可用啦。

注意:某些情况下你可能需要用到nodpi后缀,因为你可能不想要它被缩放,或者你想要在运行时通过代码手动缩放它。 

4.2.2设计可供选择的Layouts和drawables

我们应该根据于自己应用程序的需求来创建可供选择的资源类型。通常,你应该使用大小和方向后缀来提供可选择的Layout资源,使用密度后缀来提供可选择的位图drawable资源

可供选择的layouts

你应该知道你的应用是否有这个需求,是否需要根据不同的屏幕来提供可选择的布局。例如:

1.当在一个small屏幕上测试时,你发现你的布局没有完全符合你的要求或者是没有匹配好屏幕。例如一排按钮没有很好的在small屏幕上显示。这种情况你应该为small屏幕提供一个可供选择的布局用来调整按钮的大小或者位置。

2.当在一个extra large屏幕测试时, 如果没有有效利用大屏幕或者明显的被拉伸了,这种情况你也应该提供一种根据extra large屏幕的布局,并且最好再优化它以适应将来的平板设备

虽然你的App没有提供大屏幕的布局运行起来也没问题,但UI被明显拉伸的感觉会让用户觉得这个应用不太精致,用户体验会大打折扣

3.当在横屏和竖屏对比测试时, 你应该注意到UI元素在竖屏下是在底部,横屏下却是右边。如果你一直需要都在底部的话,你也应该配置后缀

总的来说,你应该确保你的应用布局如下:

(1)适用于小屏幕

(2)优化应用程序使之在大屏幕上利用额外的空间

(3)优化横竖屏

如果你的UI使用位图,那么你应该使用nine-patch 位图文件。nine-patch 这是android提供的一种格式并且它还提供一种根据让你吧美工出的图片稍微修饰。这种格式能允许你设置可以缩放的2D范围。当系统需要缩放时,系统会在你指定区域缩放 Nine-Patch位图。这样在不同的屏幕大小下你就不需要提供不同的位图资源了(可以节省APK的大小)。因为Nine-Patch会自动调整它的。但在不同的屏幕密度下你应该还是提供可供选择的Nine-Patch 文件。这样你的应用才会更健壮。关于nine-patch的使用我们以后会在第二大篇的图形章详细讲述。

可供选择的drawables

几乎每一个应用都会根据不同的密度提供可选择的drawable资源,因为这对于用户体验和UI真的很重要。下面我们来看一下图4-4的密度转换图

 

图4-4 根据每一个密度下drawable下位图的相对大小(目前4种技能应该够用了)

注意: 根据密度你仅需要提供drawable下的位图文件,如果你使用Xml来定义shape,colors或者其他drawable的资源,你就应该放到"drawable/"默认目录下

根据不同的密度提供可选择的位图drawable资源时,你应该在四种密度下使用3:4:6:8的缩放比。读者可以参考工程下每个不同的drawable里的icon尺寸:

36x36 ldpi

48x48 mdpi

72x72 hdpi

96x96 xhdpi

 

4.3关于Android3.2平板上的布局

第一代平板运行于android3.0上,一般是使用xlarge配置后缀(res/layout-xlarge/)来声明平板布局。为了提供其他类型的平板和屏幕大小----尤其是7寸平板!android3.2引入了一种新的方法来为更多离散的屏幕大小来指定资源。你的布局需要一种新的基于控件容量的技术(例如指定600dp的宽度),而不是试图使你的布局去适应那些android概括性的分组(例如large或xlarge)

当android去概括大小分组时,对于7寸平板他们也很棘手,因为5寸手机和7寸平板使用同样的large组。这两个设备在size上表面上看起来更贴近彼此,但其实UI在相当大程度上还是不同的,用户体验的效果也不太好。因此一个7寸和5寸的屏幕将不使用同样的layout。根据这两种屏幕你应该尽可能的提供不同的布局,android现在允许我们指定基于宽度或高度layout资源,记住请使用dp单位。

例如, 在你根据平板风格设计布局以后,你可能需要当屏幕小于600dp的宽度时候停止工作。这个界限会因为你的平板布局而变成最小的尺寸。这样你能立刻指定当至少宽度在600dp时候,layout中的资源文件才被用到。

注意: 记住!dip是一个非常重要的单位,你的布局大小也应该用dp单位。因为它可以看做一种比例单位而非像素那样的绝对单位。

4.3.1使用新的大小后缀(非密度)

你能为你的布局指定不同的资源配置。在下方的表格2中这些新的后缀提供给你更多的控制,相对于传统的(small, normal, large, and xlarge)这些已经超过它们了

注意: 你指定使用的这些后缀并非实际的屏幕大小。当然,这些大小是根据宽度(dp单位)和高度(dp单位)被用于activity的window中。window的特殊性在于它方法是有个动作条,用来显示电池和通知信息的。所以你在设计UI的时候需要考虑 你的应用是否需要显示动作条。下面我们看下面表格4-2:

屏幕 配置

后缀值

描述

最小宽度

sw<N>dp

例如:
sw600dp
sw720dp

用这个后缀可以确保不管当前屏幕是否横竖屏。你的应用有一个至少<N>dp的可用宽度 

例如, 如果你的layout一直需求最小屏幕的一边为600dp,那么你能使用这个后缀创建layout资源(res/layout-sw600dp/)。对于用户来讲,不管600dp是宽还是高,仅当屏幕可用的最小尺寸至少是600dp时,系统会使用这些资源(就是说你的设备不用以什么角度看,长和宽的某一边的最小值大于或等于600dp时,系统就会使用)。当屏幕水平方向改变时,设备的最小宽度不会改变

设备的最小宽度要考虑屏幕的装饰和系统UI。例如,如果设备有一些持续不变的沿着你最小宽度的轴方向的UI元素(动作条等),那么系统会宣布最小宽度比实际屏幕宽度要小,因为那些UI元素对于你的UI来说是不可用的

 因为宽度是经常影响布局的一个重要因素,所以使用最小宽度来控制一般的屏幕大小(针对平板)还是有用的。 可用宽度也是决定是否使用单屏幕布局(手机)和多屏幕布局(平板)关键因素,因此你很可能关心每一个设备上的最小方面。

屏幕可用宽度

w<N>dp

例如:
w720dp
w1024dp

用dp单位指定一个最小化的在可用宽度下可以使用的资源。系统会根据宽度改变(横竖屏切换时)来匹配这个值,并反映到当前实际可用的宽度上

当你决定是否使用多屏幕布局时它很有用,因为即使在平板设备上,你也经常不想要多屏幕布局会根据横竖屏来变化。因此你可以用这个来指定最小化的宽度需求,它可以用来代替方向后缀(land,port)和大小后缀(small,normal,large,xlarge)使之整合到一起。

屏幕可用高度

h<N>dp

例如:
h720dp
h1024dp
etc.

用dp单位指定一个最小化的屏幕高度。和"屏幕可用宽度"类似

表格4-2 android3.2加入的新后缀(注意是基于屏幕大小的而不是密度)

当使用这些后缀时你可能觉得比传统的那四种后缀复杂些,实际上你用过后就发现它很简单,它能一次简化你的UI需求。当你设计UI时,主要的事情就是需要考虑我们App中的实际大小,并且对于手机和平板风格的切换。它取决于你某些特定切换点的设计,可能对于平板布局你需要720dp的宽度,可能600dp就足够了,或者480,或者这两个之间。使用表格2的后缀,你可以在布局改变时精确的控制大小。以后我们会根据实际开发来讲述的

4.3.2关于实际配置的例子(非密度)

以下是一些屏幕数据:

320dp: 一种手机屏幕(240x320 ldpi, 320x480 mdpi, 480x800 hdpi, 等).

480dp: 一种平板 (480x800 mdpi).

600dp:  7寸平板(600x1024 mdpi).

720dp: 10寸平板(720x1280 mdpi, 800x1280 mdpi, 等).

我们使用表格2中的后缀来为我们的应用定义不同的切换风格(包括手机和平板),例如, 如果我们的平板布局600dp是最小可用宽度,我们能提供两套布局方式:

res/layout/main_activity.xml           # 手机
res/layout-sw600dp/main_activity.xml   # 平板

这种情况下,屏幕可用的最小宽度为600dp,这是伪了支持平板布局被应用。

或者你可能想要区分7寸和10寸平板,你可以这么做:

res/layout/main_activity.xml           # 手机(比600dp更小的可用宽度)
res/layout-sw600dp/main_activity.xml   # 7寸平板 (600dp的宽度或者更大)
res/layout-sw720dp/main_activity.xml   # 10寸平板(720dp的宽度或者更大)

注意上面的两套例子的使用使用 sw(最小宽度)后缀,它指定屏幕的两边中的最小一边,不管屏幕的水平方向。它忽视横竖屏。

然而某些情况下我们需要精确布局。例如如果你有一个 2个面板合并在一起的显示效果。是否屏幕提供至少600dp的宽度,是否横竖屏你都要使用它。就应该这:

res/layout/main_activity.xml         # 手机(小于600dp的可用宽度)
res/layout-w600dp/main_activity.xml  #多面板 (任意一个面板都是600dp或者更高的宽度)

注意上面这里用的是w<n>dp。实际设备可能需要两套布局,它依赖于屏幕的水平方向(一边至少是600dp的宽度,另一边小于600dp,你会发现不管横竖屏都满足这个条件。所以你需要准备两套关于横竖屏的布局)

关于什么是多屏手机或者叫多面板手机。我发个图给大家看看就明白了(下面这个手机其实有3屏),如图4-5所示:

 

图4-5 三屏手机截图

4.4实践中应该注意哪些

在多样的屏幕中我们使用传统的四种配置还是能很好的获得支持的。上面我们提供了多种定义的方法。添加这些后缀能确保你的App能适应不同的屏幕设备。下面是一些方法,告诉你如何确保你的应用程序可以正确地显示在不同的屏幕上:

1.在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位

2.在你的程序代码中最好不要使用像素(px)这种硬编码

3.不要使用AbsoluteLayout (绝对布局)

4.提供不同的位图drawable资源来适应不同的屏幕密度.

1. 使用在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位

当在XML中为你的Views定义android:layout_width和android:layout_width时,使用"wrap_content","fill_parent"或者dp单位来保证View在当前屏幕上获得一个合适的大小。

例如,一个view的宽为layout_width="100dp"   在mdpi的屏幕下它是100px,在hdpi的屏幕下它就是150px,但显示出来的效果在物理屏幕上是一样的大小

同样, 对于定义文本的大小,我们应该用sp(scale-independent pixel)。 因为sp和dp的概念是一样的,它们不是绝对的像素值

2.在你的程序代码中最好不要使用像素(px)这种硬编码

由于执行原因并使代码更简单,android系统使用像素作为标准单位用来描述尺寸或坐标值。就是说在代码中android还是使用的像素用来表述尺寸,但它是基于当前屏幕密度的,所以是变化的。如果代码中 View.getWidth()返回的值是10,这么这个10的单位为像素,但这仅仅是在某一个密度的屏幕上而已,其他不同密度的屏幕上它的结果就会不一样了。所以android是不建议我们在代码中用像素来设置布局的,因为它会加重我们的工作量,并且处理的也不一定很好。想象一下这么多屏幕设备你如果用代码来适配的话,你就得考虑的非常严谨了。不过如果你只针对某一种屏幕的话就另当别论了,但这种情况很少见。

3.不要使用AbsoluteLayout (绝对布局)

请不要使用AbsoluteLayout(绝对布局)这种布局是早期android的版本,在android1.5版本的时候就已经废弃了。虽然为了兼容以前很老的设备这种布局还存在,但目前来说,我们已经完全没有必要再使用它了。 

4.提供不同的位图drawable资源来适应不同的屏幕密度

虽然在当前屏幕配置上系统会自动缩放你的layout和drawable资源,但为了优化某些特定密度的设备,可能我们并不想让它缩放,我们想给这种密度指定一套资源也是可以而且这样的效果也很不错,能更好的根据不同的屏幕来调整我们的UI。这个我们在本文中已经反复提到了,目的就是为了加深你的印象。

一个很好的例子就是关于你用eclipse生成android工程的时候,三种不同的drawable(res/drawable-ldpi/icon.png,res/drawable-mdpi/icon.png,res/drawable-hdpi/icon.png)中都

会自动生成不同尺寸的icon.png。

注意: 如果我们没有定义某个后缀,但屏幕密度又是需要那个后缀的话,那么系统会假定你的资源都是基于mdpi(默认)的。并会放缩你的资源

4.5 关于密度额外的注意事项

这一段主要描述android在不同屏幕密度上是怎么缩放位图drawable的,我们需要更进一步的掌握系统控制位图资源的原理。当在运行时操作图形,我们能更好的理解它是怎样支持多屏幕的,我们应该了解下系统是如何保证适应屏幕并适当的缩放位图的:

1.载入时自动缩放 (比如位图drawables)。

基于当前屏幕的密度,系统使用任意大小或者密度的资源来显示它们的时候是没有缩放过的。如果在当前密度下没有可用的资源,那么系统会载入默认资源并等比缩放它们来匹配当前屏幕的密度。除非有有针对密度的后缀出现,不然系统都是认为默认资源(没有后缀的drawable)是为mdpi的密度来设计的。因此系统这个时候会调整位图的大小来适应屏幕。

如果你需求资源缩放之前的大小,那系统实际上返回的是缩放后的大小。例如:如果你没有指定hdpi后缀,一个在mdpi下50px*50px的位图在一个hdpi下会缩放为75px*75px。在hdpi屏幕下系统会返回这个75px这个大小。如果你不希望系统根据不同的密度来缩放资源,那么请记住使用“res/drawable-nodpi/”

2.运行时自动缩放(比如像素的大小和坐标)

一个应用能关闭载入时自动缩放的这个功能,只要你在manifest中设置android:anyDensity ="false",或者在程序中使用BitmapFactory.Options.inScaled返回的值为false。在这种情况下。系统会在绘制的时候自动等比缩放任何绝对坐标和像素大小。这么做确保用针对像素的屏幕元素能一直显示。系统会处理缩放,然后转化并报告缩放的像素大小,而不是物理像素大小。下面我们来举个例子:例如,假设一个设备有一个WVGA(800*480)下是hdpi的屏幕,并且它和在传统的HVGA(480*320)屏幕一样的物理大小,但执行应用的时候关闭了载入时自动缩放这个功能。这样的话,当系统查询屏幕大小时,它会报告一个320*533的大小,为什么会是320*533呢,我们开的模拟器是800*480的啊!!因为系统在绘制的时候缩放了。系统报告的是缩放后的像素大小,而不是我们模拟器上的物理大小。然后App需要绘制操作时,本来想在(10,10)上的位置绘制,但会变成(15,15)。如果你的应用直接操作缩放位图,那这种差异可能导致意外的行为。以前很多刚开始接触android的朋友经常会遇到系统报告320*533的这种问题。原因就是由于我们关闭了载入时自动缩放这个功能。你关闭了它的话,它就会在运行时缩放并返回结果。所以我们要检查manifest中是否设置android:anyDensity ="false"。如果有赶快去掉。也许还有朋友发现没有设置怎么也会出现这种问题,这是由于以前比较老的SDK版本系统默认设置关闭了载入时自动缩放这个功能导致的,不过目前这种情况很少发生了。 

4.5.1运行时创建缩放的位图对象

我们的应用如果需要在内存中创建一个位图(bitmap)对象,那么系统会认为你是基于mdpi密度的屏幕来创建的。默认的,系统会根据屏幕密度在绘制的时候自动缩放这个位图。当位图没有指定密度属性时候,系统会自动缩放。为了控制位图在运行时创建后是否被缩放,我们可以指定位图的密度属性,使用Bitmap.setDensity()。具体的值可以传DisplayMetrics.DENSITY_HIGH或其他。我们还可以从文件或者一个流中使用BitmapFactory来创建位图,使用BitmapFactory.Options来定义位图的属性,那么系统会根据你的属性来缩放它。我们还可以使用BitmapFactory.Options.inDensity来指定这个位图是否需要匹配当前的屏幕密度。如果我们设置BitmapFactory.Options.inDensity=false;那么系统在载入位图时将不会自动缩放它,只会在运行时缩放它。使用运行时缩放CPU占用率高,内存占用低。使用载入时缩放CPU占用率低,内存占用高。如何取舍就看你的需求了。

4.5.2 dp和像素的转换

有些情况下,dp和px只需要相互转换的。例如一个应用在用手指滑动屏幕的时候会感应用户的手指移动了多少个像素。在一个normal和mdpi的屏幕下,用户必须移动16px/160dpi,等价于十分之一英寸(大约2.5mm(毫米))。那么在一个hdpi(240dpi)的屏幕上用户必须移动16px/20dpi,等价十五分之一英寸(1.7mm)。距离是很短的,因此这对用来说会很敏感。为了修复这个问题,需要用代码吧dp转换成px单位。例如:

 

// 手势的响应的范围(dp)

privatestaticfinalfloat GESTURE_THRESHOLD_DP =16.0f// 获取屏幕的密度来缩放

finalfloat scale =getResources().getDisplayMetrics().density;

//根据密度吧dp转换成px

mGestureThreshold =(int)(GESTURE_THRESHOLD_DP * scale +0.5f);

// 可以使用这个响应的范围了

 

 

这里DisplayMetrics.density的值为(0.75[ldpi],1[mdpi],1.5[hdpi],2[xhdpi]) ,其实我们还可以使用ViewConfiguration类来处理,但前提是打开了载入时缩放这个功能(目前来说,默认都

是开的不用担心)并且我们可以使用ViewConfiguration.getScaledTouchSlop()来直接获得换算距离。具体使用如下:

private static final int GESTURE_THRESHOLD_DP =ViewConfiguration.get(myContext).getScaledTouchSlop();

 

4.6 如何在多种屏幕下测试我们的应用

首先我们来看一下图4-6,这是使用AVD工具设置虚拟设备的截图

 

图4-6 这是android SDK中的AVD工具,它提供一套虚拟机设备。(最后1,2章会详细讲述使用方法)

在发布我们的应用之前,我应该在不同的屏幕大小和密度上测试我们的App。android SDK中有很多模拟器,我们可以修改模拟器上的默认的大小,密度,分辨率来整合测试。例如使用android2.1版本的模拟器,我们可以同时新建多个2.1的版本,但每个的密度不同。这么多模拟器可以为我们省下很多钱来买不同的手机。如果你想修改某个模拟器的密度,你只需要选中它点击右边的edit就可以修改了。如下图4-7所示:(这里只需要留点印象后面会有章节单独的讲述AVD参数配置)

 

图4-7 在AVD中点击edit后的截图

为了测试应用是否支持多种不同的屏幕,我们应该创建一系列的AVD(android virtual devices)。如果不用eclipse的话,我们可以在sdk目录下执行SDK Manager.exe这样就显示出图4-6的界面。

4.7 本章小节

关于android的屏幕和分辨率,这是非常重要的一个知识点,希望大家认真学好它,在以后的开发过程中绝对是非常有帮助。现在我们稍微回顾一下关键的内容:

一,Boss的5个关键点

1.boss的体型(屏幕大小)

2.boss的模型细粒度(屏幕密度)

3.我们看boss的角度(屏幕的方向)

4.boss的总体外观和风格(屏幕的分辨率)

5.boss的伤害公式(dp单位)

二,我们也有相应的对策对付他们

drawable(我们游戏角色的技能,目前4种(ldpi,mdpi,hdpi,xhdpi))

layout(我们游戏角色的装备,目前4种(small,normal,large,extra large))

三,怎么样搞定这些boss

manifest文件中指出你的App支持的屏幕大小

根据不同的屏幕大小提供不同的布局

根据不同的屏幕密度提供不同的位图

四,最后我们需要熟悉后缀和一些公式(就像游戏boss的攻略)

目前那么多后缀不需要完全记住(以后用的多自然就记住了),但需要有个印象(后缀可以为drawable服务,也可以为layout服务,你可以想象成后缀是一种附魔提升装备(layout)的属性,又是一种铭文用来提高技能(drawable)伤害或效果)

还记得3:4:6:8吗? 还记得 0.75x, x ,1.5x, 2x吗?这些都很重要哦

本文来自jy02432443,QQ78117253。转载请保留出处,并保留追究法律责任的权利

posted @ 2013-11-18 19:40  jy02432443  阅读(3177)  评论(1编辑  收藏  举报