多屏幕多分辨率的支持和一些概念
标签: androidlayoutui手机测试eclipse
2012-06-01 16:18 6919人阅读 评论(6) 收藏 举报
分类:
Android(385)
快速预览
- Android 在不同的屏幕大小和密度上执行
- 屏幕的显示会影响用户界面的效果
- 系统处理大多数的自适应屏幕
- 你应该针对屏幕创建资源以更好的控制你的UI
Android 是一个系统,运行在这个系统上的很多设备它们的屏幕大小和密度都不一样。但我们需要解决这个问题。本节为详细的为你解释一些基础概念和教你怎么处理关于多屏幕多分辨率的支持。 虽然系统会根据不同的屏幕来执行缩放和调整你的App,但你还是应该自己在加点外力来最大化用户体验的效果。
多屏幕支持概述
以下是一些术语和概念,大家要稍微留心看看:
屏幕大小
根据屏幕对角线实际测量的物理大小(android已经根据屏幕大小分为四类small, normal, large, extra large)
屏幕密度
屏幕物理区域一个单位(一般用英寸)的像素数量,一般简称dpi(每英寸上的点数,记住DPI这个概念很重要)
这里android也分为四类 low, medium, high, extra high.
方向
从用户的角度看设备只有2个方向,那就是横屏和竖屏。关于横竖屏最好也要出一套相对应的UI,因为长宽比不一样,放心难度不是很大。
分辨率
一个物理屏幕上像素点的总数。一般我们的App不会直接使用分辨率,而是使用密度和屏幕大小
独立像素单位(Density-independent pixel (dp))
一个虚拟的单位,用来定于UI布局的。用来表述布局的尺寸或者位置。
dip(注 意dip是独立像素单位,dpi是屏幕密度)等价于一个160dpi(mdpi)的屏幕,运行时系统会根据你实际屏幕的密度来自动处理缩放。像素和dip 的转换公式为dp =px*(dpi/160) 。例如在240密度(dpi)的屏幕上一个dp等于1.5个像素(dp=px*1.5)。以后我们应该尽量使用dip单位布局,不要使用像素单位。这样会使你的app屏幕兼容性更好
多屏幕可以看成是许多Boss,要战胜这些boss有5点我们需要攻破它,分别是屏幕大小,屏幕密度,方向,分辨率,独立像素单位。
屏幕大小-Boss的外在体型:熟悉每个boss的体型,会让我们采取不同的战术,大的笨重,小的轻巧
屏幕密度-Boss的密度:别看有个boss体型很小,他密度高,一个俄罗斯梅花坐直接压死你,我们需要知道每个boss的密度。
方向-Boss的体型比:有的boss矮胖,看上去就像一个游戏手柄,但有些高瘦,看上去就像一根棍子。但记住我们是站着看的,但如果躺着看呢,所以角度不同看出来的结果就可能不一样
分辨率-boss的可识别度:隔boss近的话分辨率低,只能看到boss的某个部位而且很大,我不知道这是哪个boss;远的话分辨率高,能看到boss的所有部位,哦!原来这个boss就是伊利丹怒风
独立像素单位-boss的攻击力:每个boss的攻击技能都不太一样,所以我们需要吧所有boss的技能转换为攻击力来方便我们计算对付boss的策略
多屏幕支持的范围
android1.6开始就支持多屏幕和密度了,这都是为了更好的用户体验,为了简化我们多屏幕的开发。android提供一组范围让我们使用:
- 关于屏幕大小: small, normal, large, xlarge(看来目前boss的体型就这四种)
注意: 关于android3.2会有些不同,以后我们在讲平板和手机支持时会说明
- 屏幕密度: ldpi (low), mdpi (medium), hdpi (high), xhdpi (extra high) (so 密度也是四种)
这些广义的大小和密度都是基于normal大小和mdpi来配置的或者说相对论。一般来说我们手机800*480或者854*480 密度都是240(hdpi),屏幕大小为large。
每一个大小和密度都有一个跨越幅度。但这些幅度中具体的微调我们是不用关心的,android会帮我们处理,下面我们来看图
我们设计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两种密度,正常的屏幕密度应该是hdpi。但不用担心我们是有办法解决这种问题的。
就是说boss体型大不一定密度高,万一它里面真空呢。但也不要小看体型小的boss,有可能它里面装的都是金属,你会很纠结怎么打败它。还有一些特殊的boss他体型一样,但密度可以变化,真牛B的boss啊!先来个真空上阵,再直接用铁砸死你。
独立密度
维持独立密度是很重要的,因为没有它,一个UI元素(比如一个按钮)会出现在一个物理屏幕很大,但密度很少的显示效果(看起来就是像是被强行放大的,失真很严重)。这样相对位置的改变会出问题
图1是你的App没有支持不同密度产生的效果
图2是具有良好的支持程序为例产生的效果
android 系统帮助你的App实现独立密度的方法有两种:
- 系统缩放dp单位来适用当前屏幕密度The system scales dp units as appropriate for the current screen density
- 系统基于当前屏幕密度缩放drawable资源到适用的大小
图 1是用像素单位来定义的大小。 你发现他们的布局完全变了,有的大有的小。这是因为他们的实际屏幕大小可能是一样的,高密度屏幕每英寸的像素更多(所以你会发现像素一样,但高密度的设备显示出来的效果却很小)。图2使用的是dp单位,基于mdpi密度的设备它不会变化,ldpi它会自动缩小,hdpi它会自动放大。所以效果非常好。图片 显示出来的样子和大小完全一样。
使用密度系统会缩放图片使之看上去完全一样,但细心的你会发现有些图片变模糊了。所以我们应该提供可选择的图片资源让系统来选择以适应不同的密度。所以以后工程项目中hdpi,mdpi,ldpi三个文件夹就是用在这里的。这样就不会有模糊的感觉了,perfect!!
我们一定要换算boss的攻击力,好躲避技能,它的技能实在太多了,搞不好一口水就能淹死你(强力的攻击啊)。
怎样支持多屏幕(怎么搞定这么多boss)
以上我们学习了这么多的理论基础。我们应该优雅的处理它们了。
- 1.明确的在manifest文件中指出你的App支持的屏幕大小(哈哈,抓到你的小辫子了吧)
我们可以有选择性的过滤掉一些老掉牙的设备,以减轻我们的工作量。但这并不是一个好的方法仅供参考。在manifest 中使用节点<supports-screens>
- 2.根据不同的屏幕大小提供不同的布局(我们根据不同的boss体型来换不同的装备应对,暗影系boss需要多穿点暗抗装啊)
默认的android会调整你App的布局来使用当前设备的屏幕。大多数情况下可以满足,但某些特殊情况,你的UI可能需要一个更精确的调整。例如,一个larger 屏幕,你可能想要利用额外的屏幕控件调整位置和一些元素的大小,或者一个小屏幕中,你可能需要调整到刚好合适。比如你需要适应一个extra large 屏幕。你可以在文件夹layout-xlarge下放置布局(大家记住一般layut下放的都是xml布局文件)
Android 3.2开始,有一种新的放置方式。例如你的平板需求至少600dp的屏幕宽度,你可以使用layout-sw600dp 这种文件夹放置。以后我们在平板一节会详细描述
- 3.根据不同的屏幕密度提供不同的位图(根据不同的boss密度来学技能,你敢压我?我有闪避 谢谢!)
默认android会缩放你的位图(.png.jpg.gif),还有一种android专用的Nine-Patch(.9.png)。例如,你的App提供的位图仅仅为标准密度(mdpi),那么在hdpi的屏幕上这些位置会被等比放大。所以我们应该有选择性的对于不同的屏幕密度包含不同的分辨率。
所以android针对图片资源配置后缀,前面我们知道的4种密度(ldpi.mdpi,hdpi,xhpi),如果是ldpi 我们应该使用drawable- ldpi这种文件夹。以上关于屏幕大小和密度的后缀都符合屏幕支持的那些范围。
注意: 如果你没有熟悉的配置后缀, 不知道如何使用它们,没关系。我们以后会在App资源中讲解!
在运行时, 对于给定资源系统使用以下这些步骤保证你最佳的显示:
- 系统会使用合适的替代资源
基于当前屏幕的大小和密度,系统会针对屏幕的密度和大小使用资源。例如,如果设备有一个 hdpi的屏幕那么当App请求一个drawable 资源时,系统会在最匹配的的设备配置下的drawable 资源目录寻找。可以取决于其他可替代的资源,使用drawable-hdpi的资源目录最匹配的,所以系统会使用drawable-hdpi目录下的资源。
- 如果没有可用的匹配资源,系统会使用默认资源等比放大或缩小以匹配当前屏幕的大小和密度。
这里的默认资源是没有后缀的。例如,“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资源就行了。
使用配置后缀
Android支持几种配置后缀,它允许你控制系统有选择性的筛选基于当前屏幕设备的资源文件。一个配置后缀就是一个String你可以在资源目录文件夹下追加指定的后缀即可 。
使用一个配置后缀的方法步骤:
- 在你的工程res目录下创建一个新的目录,格式为<资源种类名>-<后缀>:
- <资源种类名>是标准的资源名字(例如 drawable或者layout)
- <后缀> 是指定屏幕配置使用哪一种资源(例如hdpi或者xlarge)
我们可以使用的后缀不仅仅只有一个哦,在一个文件夹下我们可以同时使用多种后缀来限定资源,使用"-"即可。
- 在新的目录中保存合适资源,资源文件的命名必须和默认资源文件一样。
例如 xlarge是一个后缀用于extra large 屏幕。当你追加后缀时(例如layout-xlarge) ,它说明系统会在extra large屏幕上使用这些资源
表格1.
装备,技能齐全啊。求秒杀boss!!!
屏幕特征 | 后缀 | 描述 |
---|---|---|
大小 | 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。 | |
方向 | land | 资源用于横屏 |
port | 资源用于竖屏 | |
长宽比 | long | 资源用于长宽比相差很远的配置(相对于4寸(normal)屏幕左右基准屏幕) |
notlong | 资源用于长宽比差不多的配置(相对论,同上) |
注意: 如果对于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后缀,因为你可能不想要它被缩放,或者你想要在运行时通过代码手动缩放它。
设计可供选择的layouts(装备)和drawables(技能)
你应该根据于你App的需求来创建可供选择的资源类型。通常,你应该使用大小和方向后缀来提供可选择的Layout资源,使用密度后缀来提供可选择的位图drawable资源
可供选择的布局(layouts)
你应该知道你的App是否有这个需求,是否需要根据不同的屏幕来提供可选择的布局。例如:
- 当在一个small屏幕上测试时,你发现你的布局没有完全符合你的要求或者是没有匹配好屏幕。例如一排按钮没有很好的在small屏幕上显示。这种情况你应该为small屏幕提供一个可供选择的布局用来调整按钮的大小或者位置。
- 当在一个extra large屏幕测试时, 如果没有有效利用大屏幕或者明显的被拉伸了,这种情况你也应该提供一种根据extra large屏幕的布局,并且最好再优化它以适应将来的平板设备
虽然你的App没有提供大屏幕的布局运行起来也没问题,但UI被明显拉伸的感觉会让用户觉得这个APP不太精致,用户体验会大打折扣 - 当在横屏和竖屏对比测试时, 你应该注意到UI元素在竖屏下是在底部,横屏下却是右边。如果你一直需要都在底部的话,你也应该配置后缀
总的来说,你应该确保你的App布局如下:
- 适用于小屏幕
- 优化App使之在大屏幕上利用额外的空间
- 优化横竖屏
如果你的UI使用位图,那么你应该使用nine-patch (超牛B特殊技能,目前游戏版本没更新,以后更新了马上给学了)位图文件。nine-patch 这是android提供的一种格式并且它还提供一种根据让你吧美工出的图片稍微修饰。这种格式能允许你设置可以缩放的2D范围。当系统需要缩放时,系统会在你指定区域缩放 Nine-Patch位图。这样在不同的屏幕大小下你就不需要提供不同的位图资源了(可以节省APK的大小)。因为Nine-Patch会自动调整它的。但在不同的屏幕密度下你应该还是提供可供选择的Nine-Patch 文件。这样你的APP才会更健壮。关于nine-patch的使用我们以后会在第二大篇的图形章详细讲述。
可供选择的drawables
图3. 根据每一个密度下drawable下位图的相对大小(目前4种技能应该够用了)
几乎每一个App都会根据不同的密度提供可选择的drawable资源,因为这对于用户体验和UI真的很重要。
注意: 根据密度你仅需要提供drawable下的位图文件,如果你使用Xml来定义shape,colors或者其他drawable的资源,你就应该放到"drawable/"默认目录下
根据不同的密度提供可选择的位图drawable资源时,你应该在四种密度下使用3:4:6:8的缩放比。你可以参考工程下每个不同的drawable里的icon尺寸。以下是数据:
- 36x36 ldpi
- 48x48 mdpi
- 72x72 hdpi
- 96x96 xhdpi
关于Android 3.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单位。因为它可以看做一种比例单位而非像素那样的绝对单位。
使用新的大小后缀(非密度)
你能为你的布局指定不同的资源配置。在下方的表格2中这些新的后缀提供给你更多的控制,相对于传统的(small, normal, large, and xlarge)这些已经超过它们了
注意: 你指定使用的这些后缀并非实际的屏幕大小。当然,这些大小是根据宽度(dp单位)和高度(dp单位)被用于activity的window中。window的特殊性在于它方法是有个动作条,用来显示电池和通知信息的。所以你在设计UI的时候需要考虑 你的App是否需要显示动作条。
表格2. 以下是android3.2进入的新后缀(注意是基于屏幕大小的而不是密度)
屏幕 配置 | 后缀值 | 描述 |
---|---|---|
最小宽度 | sw<N>dp
例如: |
用这个后缀可以确保不管当前屏幕是否横竖屏。你的App有一个至少<N>dp的可用宽度 例如, 如果你的layout一直需求最小屏幕的一边为600dp,那么你能使用这个后缀创建layout资源(res/layout-sw600dp/)。对于用户来讲,不管600dp是宽还是高,仅当屏幕可用的最小尺寸至少是600dp时,系统会使用这些资源(就是说你的设备不用以什么角度看,长和宽的某一边的最小值大于或等于600dp时,系统就会使用。)。当屏幕水平方向改变时,设备的最小宽度不会改变 设备的最小宽度要考虑屏幕的装饰和系统UI。例如,如果设备有一些持续不变的沿着你最小宽度的轴方向的UI元素(动作条等),那么系统会宣布最小宽度比实际屏幕宽度要小,因为那些UI元素对于你的UI来说是不可用的 因为宽度是经常影响布局的一个重要因素,所以使用最小宽度来控制一般的屏幕大小(针对平板)还是有用的。 可用宽度也是决定是否使用单屏幕布局(手机)和多屏幕布局(平板)关键因素,因此你很可能关心每一个设备上的最小方面。 |
屏幕可用宽度 | w<N>dp
例如: |
用dp单位指定一个最小化的在可用宽度下可以使用的资源。系统会根据宽度改变(横竖屏切换时)来匹配这个值,并反映到当前实际可用的宽度上 当你决定是否使用多屏幕布局时它很有用,因为即使在平板设备上,你也经常不想要多屏幕布局会根据横竖屏来变化。因此你可以用这个来指定最小化的宽度需求,它可以用来代替方向后缀(land,port)和大小后缀(small,normal,large,xlarge)使之整合到一起。 |
屏幕可用高度 | h<N>dp
例如: |
用dp单位指定一个最小化的屏幕高度。和"屏幕可用宽度"类似 |
当使用这些后缀时你可能觉得比传统的那四种后缀复杂些,实际上你用过后就发现它很简单,它能一次简化你的UI需求。当你设计UI时,主要的事情就是需要考虑我们App中的实际大小,并且对于手机和平板风格的切换。它取决于你某些特定切换点的设计,可能对于平板布局你需要720dp的宽度,可能600dp就足够了,或者480,或者这两个之间。使用表格2的后缀,你可以在布局改变时精确的控制大小。以后我们会根据实际开发来讲述的
配置例子
以下是一些屏幕数据:
- 320dp: 一种手机屏幕(240x320 ldpi, 320x480 mdpi, 480x800 hdpi, 等).
- 480dp: 一种平板 (480x800 mdpi).
- 600dp: 7寸平板(600x1024 mdpi).
- 720dp: 10寸平板(720x1280 mdpi, 800x1280 mdpi, 等).
我们使用表格2中的后缀来为我们的App定义不同的切换风格(包括手机和平板),例如, 如果我们的平板布局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屏)
关于声明屏幕支持的大小
在android3.2的manifest 中引入了<supports-screens> 节点,以后再讲manifest 文件的时候我们会详细描述
实践中应该注意哪些
在多样的屏幕中我们使用传统的四种配置还是能很好的获得支持的。上面我们提供了多种定义的方法。添加这些后缀能确保你的App能适应不同的屏幕设备。
下面是一些方法,告诉你如何确保你的应用程序可以正确地显示在不同的屏幕上:
- 在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位
- 在你的程序代码中最好不要使用像素(px)这种硬编码
- 不要使用AbsoluteLayout (绝对布局)
- 提供不同的位图drawable资源来适应不同的屏幕密度.
1. 使用在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位(装备附魔要搞好)
当在XML中为你的Views定义android:layout_width和android:layout_height 时, 使用"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 (绝对布局)(这种装备out了,赶紧摧毁)
请不要使用AbsoluteLayout(绝对布局)这种布局是早期android的版本,在android1.5版本的时候就已经废弃了。虽然为了兼容以前很老的设备这种布局还存在,但目前来说,我们已经完全没有必要再使用它了。
4.提供不同的位图drawable资源来适应不同的屏幕密度.(根据不同的boss我们应该选相应的技能哦)
虽然在当前屏幕配置上系统会自动缩放你的layout和drawable资源,但为了优化某些特定密度的设备,可能我们并不想让它缩放,我们想给这种密度指定一套资源也是可以而且这样的效果也
很不错,能更好的根据不同的屏幕来调整我们的UI。这个我们在本文中已经反复提到了,目的就是为了加深你的印象。
一个很好的例子就是关于你用eclipse生成android工程的时候,三种不同的drawable(res/drawable-ldpi/icon.png,res/drawable-mdpi/icon.png,res/drawable-hdpi/icon.png)中都
会自动生成不同尺寸的icon.png。
注意: 如果我们没有定义某个后缀,但屏幕密度又是需要那个后缀的话,那么系统会假定你的资源都是基于mdpi(默认)的。并会放缩你的资源
关于密度额外的注意事项
这一段主要描述android在不同屏幕密度上是怎么缩放位图drawable的,我们需要更进一步的掌握系统控制位图资源的原理。
当在运行时操作图形,我们能更好的理解它是怎样支持多屏幕的,我们应该了解下系统是如何保证适应屏幕并适当的缩放位图的:
- 载入时自动缩放 (比如位图drawables)--根据boss密度自动选择技能?这真太智能了
基于当前屏幕的密度,系统使用任意大小或者密度的资源来显示它们的时候是没有缩放过的。如果在当前密度下没有可用的资源,那么系统会载入默认资源并等比缩放它们来匹配当前屏幕的密度。除非有有针对密度的后缀出现,不然系统都是认为默认资源(没有后缀的drawable)是为mdpi的密度来设计的。因此系统这个时候会调整位图的大小来适应屏幕。
如果你需求资源缩放之前的大小,那系统实际上返回的是缩放后的大小。例如:如果你没有指定hdpi后缀,一个在mdpi下50px*50px的位图在一个hdpi下会缩放为75px*75px。在hdpi屏幕下系统会返回这个75px这个大小。
如果你不希望系统根据不同的密度来缩放资源,那么请记住使用“res/drawable-nodpi/”
- 运行时自动缩放(比如像素的大小和坐标)--打boss的时候在自动选?这也挺牛B
一个App能关闭载入时自动缩放的这个功能,只要你在manifest中设置android:anyDensity ="false",或者在程序中使用BitmapFactory.Options.inScaled返回的值为false。在这种情况下。系统会在绘制的时候自动等比缩放任何绝对坐标和像素大小。这么做确保用针对像素的屏幕元素能一直显示。系统会处理缩放,然后转化并报告缩放的像素大小,而不是物理像素大小。下面我们来举个例子:例如,假设一个设备有一个WVGA(800*480)下是hdpi的屏幕,并且它和在传统的HVGA(480*320)屏幕一样的物理大小,但执行App的时候关闭了载入时自动缩放这个功能。这样的话,当系统查询屏幕大小时,它会认为谁TMD的这么坑爹。然后他会报告一个320*533的大小,为什么会是320*533呢,我们开的模拟器是800*480的啊!!因为系统在绘制的时候缩放了。系统报告的是缩放后的像素大小,而不是我们模拟器上的物理大小。然后App需要绘制操作时,本来想在(10,10)上的位置绘制,但会变成(15,15)。如果你的App直接操作缩放位图,那这种差异可能导致意外的行为。以前很多刚开始接触android的朋友经常会遇到系统报告320*533的这种问题。原因就是由于我们关闭了载入时自动缩放这个功能。你关闭了它的话,它就会在运行时缩放并返回结果。所以我们要检查manifest中是否设置android:anyDensity ="false"。如果有赶快去掉。也许还有朋友发现没有设置怎么也会出现这种问题,这是由于以前比较老的SDK版本系统默认设置关闭了载入时自动缩放这个功能导致的,不过目前这种情况很少发生了。
运行时创建缩放的位图对象
我们的App如果需要在内存中创建一个位图(bitmap)对象,那么系统会认为你是基于mdpi密度的屏幕来创建的。默认的,系统会根据屏幕密度在绘制的时候自动缩放这个位图。当位图没有指
定密度属性时候,系统会自动缩放。为了控制位图在运行时创建后是否被缩放,我们可以指定位图的密度属性,使用Bitmap.setDensity()。具体的值可以传DisplayMetrics.DENSITY_HIGH或
其他。我们还可以从文件或者一个流中使用BitmapFactory来创建位图,使用BitmapFactory.Options来定义位图的属性,那么系统会根据你的属性来缩放它。我们还可以使用
BitmapFactory.Options.inDensity来指定这个位图是否需要匹配当前的屏幕密度。如果我们设置BitmapFactory.Options.inDensity=false;那么系统在载入位图时将不会自动缩放它,只会在
运行时缩放它。使用运行时缩放CPU占用率高,内存占用低。使用载入时缩放CPU占用率低,内存占用高。如何取舍就看你的需求了。
dp 和 像素的转换(攻击力有时候还需要转成技能的)
有些情况下,dp和px只需要相互转换的。例如一个App在用手指滑动屏幕的时候会感应用户的手指移动了多少个像素。在一个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()来直接获得换算距离。具体使用如下:
privatestaticfinalint GESTURE_THRESHOLD_DP =ViewConfiguration.get(myContext).getScaledTouchSlop();
在多种屏幕下怎样测试我们的App
图5. 这是android SDK中的AVD工具,它提供一套虚拟机设备。上面这些都是通过你点击"new"建立的,不是默认就有的哦,以后会详细讲述使用方法的
在发布我们的应用之前,我应该在不同的屏幕大小和密度上测试我们的App。android SDK中有很多模拟器,我们可以修改模拟器上的默认的大小,密度,分辨率来整合测试。例如使用android2.1版本的模拟器,我们可以同时新建多个2.1的版本,但每个的密度不同。这么多模拟器可以为我们省下很多钱来买不同的手机。如果你想修改某个模拟器的密度,你只需要选中它点击右边的edit就可以修改了。如下图所示:
为了测试App是否支持多种不同的屏幕,我们应该创建一系列的AVD(android virtual devices)。如果不用eclipse的话,我们可以在sdk目录下执行SDK Manager.exe这样就显示出图5的界面
关于android虚拟机的管理和使用我们以后会详细描述。
表格3. 以下是模拟器中多种可用的配置(加粗的是模拟器中自带的,如果需要其他就需要手动设置下了)
Low density (120), ldpi | Medium density (160), mdpi | High density (240), hdpi | Extra high density (320), xhdpi | |
---|---|---|---|---|
Small screen | QVGA (240x320) | 480x640 | ||
Normal screen | WQVGA400 (240x400) WQVGA432 (240x432) |
HVGA (320x480) | WVGA800 (480x800) WVGA854 (480x854) 600x1024 |
640x960 |
Large screen | WVGA800(480x800) WVGA854 (480x854) |
WVGA800(480x800) WVGA854 (480x854) 600x1024 |
||
Extra Large screen | 1024x600 | WXGA (1280x800)? 1024x768 1280x768 |
1536x1152 1920x1152 1920x1200 |
2048x1536 2560x1536 2560x1600 |
? 表示用于android3.0的平台 |
图6. 这是设置我们模拟器开启后的大小,一般默认的模拟在电脑上打开显然很大,如果你需要缩小的话就条市政以下scale即可
建议大家启动模拟器的时候最好不要缩放,这样看起来的结果是物理屏幕上区别就不会很大了。当你从AVD Manager启动一个模拟器时,我们指定的dpi的时候,可以和自己显示器匹配下。
如果我们需要使用命令启动的话,可以这样启动:
emulator -avd <avd_name>-scale 96dpi