Android自适应不同分辨率或不同屏幕大小的layout布局(横屏|竖屏)
一:不同的layout
Android手机屏幕大小不一,有480x320, 640x360, 800x480.怎样才能让App自动适应不同的屏幕呢?
其实很简单,只需要在res目录下创建不同的layout文件夹,比如layout-640x360,layout-800x480,所有的layout文件在编译之后都会写入R.java里,而系统会根据屏幕的大小自己选择合适的layout进行使用。
二:hdpi、mdpi、ldpi
在之前的版本中,只有一个drawable,而2.1版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三个,这三个主要是为了支持多分辨率。
drawable- hdpi、drawable- mdpi、drawable-ldpi的区别:
(1)drawable-hdpi里面存放高分辨率的图片,如WVGA (480x800),FWVGA (480x854)
(2)drawable-mdpi里面存放中等分辨率的图片,如HVGA (320x480)
(3)drawable-ldpi里面存放低分辨率的图片,如QVGA (240x320)
系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片。
更正:应该是对应不同density 的图片
在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片。
[i]备注:三者的解析度不一样,就像你把电脑的分辨率调低,图片会变大一样,反之分辨率高,图片缩小。 [/i]
屏幕方向:
横屏竖屏自动切换:
可以在res目录下建立layout-port-800x600和layout-land两个目录,里面分别放置竖屏和横屏两种布局文件,这样在手机屏幕方向变化的时候系统会自动调用相应的布局文件,避免一种布局文件无法满足两种屏幕显示的问题。
不同分辨率横屏竖屏自动切换:
以800x600为例
可以在res目录下建立layout-port-800x600和layout-land-800x600两个目录
不切换:
以下步骤是网上流传的,不过我自己之前是通过图形化界面实现这个配置,算是殊途同归,有空我会把图片贴上来。
还要说明一点:每个activity都有这个属性screenOrientation,每个activity都需要设置,可以设置为竖屏(portrait),也可以设置为无重力感应(nosensor)。
要让程序界面保持一个方向,不随手机方向转动而变化的处理办法:
在AndroidManifest.xml里面配置一下就可以了。加入这一行android:screenOrientation="landscape"。
例如(landscape是横向,portrait是纵向):
Java代码:
android:versionCode="1"
android:versionName="1.0">
android:screenOrientation="portrait">
另外,android中每次屏幕的切换动会重启Activity,所以应该在Activity销毁前保存当前活动的状态,在Activity再次Create的时候载入配置,那样,进行中的游戏就不会自动重启了!
有的程序适合从竖屏切换到横屏,或者反过来,这个时候怎么办呢?可以在配置Activity的地方进行如下的配置android:screenOrientation="portrait"。这样就可以保证是竖屏总是竖屏了,或者landscape横向。
而有的程序是适合横竖屏切换的。如何处理呢?首先要在配置Activity的时候进行如下的配置:android:configChanges="keyboardHidden|orientation",另外需要重写Activity的 onConfigurationChanged方法。实现方式如下,不需要做太多的内容:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// land do nothing is ok
} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
// port do nothing is ok
}
}
写一个支持多分辨的程序,基于1.6开发的,建立了三个资源文件夹drawable-hdpi drawable-mdpi drawable-ldpi,里面分别存放72*72 48*48 36*36的icon图标文件。当我在G1(1.5的系统)上测试时,图标应该自适应为48*48才对啊,但实际显示的是36*36。怎么才能让其自适应 48*48的icon图标呢
解决办法 drawable-hdpi drawable-mdpi drawable-ldpi改成drawable-480X320 drawable-800X480的多分辨支持的文件夹
对于Android游戏开发我们不得不像iPhone那样思考兼容 Android平板电脑,对于苹果要考虑iPad、iPhone 3GS和iPhone 4等屏幕之间的兼容性,对于几乎所有的分辨率总结了大约超过20中粉笔阿女郎的大小和对应关系,对于开发Android游戏而言可以考虑到未来的3.0以及很多平板电脑的需要。
常规的我们可能只考虑QVGA,HVGA,WVGA,FWVGA和DVGA,但是抛去了手机不谈,可能平板使用类似WSVGA的1024×576以及WXGA的1280×768等等。
QVGA = 320 * 240;
WQVGA = 320 * 480;
WQVGA2 = 400 * 240;
WQVGA3 = 432 * 240;
HVGA = 480 * 320;
VGA = 640 * 480;
WVGA = 800 * 480;
WVGA2 = 768 * 480;
FWVGA = 854 * 480;
DVGA = 960 * 640;
PAL = 576 * 520;
NTSC = 486 * 440;
SVGA = 800 * 600;
WSVGA [...]
这是一个比较有代表性的Android软件资源包,drawable里面存放的是应用的图标文件,layout存放的是布局,简单说就是这些图标如何摆放。为什么Android上需要这么多资源包文件和布局文件是我们接下来需要讨论的问题。
Android设备屏幕的尺寸是各式各样的,如小米是4英寸的,Xoom平板是10英寸;分辨率也千奇百怪,800×480,960×540等;Android版本的碎片化问题更是萦绕于心,不过在设计应用时可以分为两大块:3.0之前的版本和3.0之后的版本。这种情况会带来什么问题我们用三个假设来说明一下。
1. 假设你的手上有两个4英寸的设备,设备A的分辨率是800×480,设备B的分辨率是1600×960。你在设备A上设计了一个64×64像素的图标,感觉它大小正合适,但放到设备B上的时候,这个图标看上去就只有之前一半大小了。
2. 假设你手上的两个设备,设备A是4英寸,设备B是10英寸。在设备A上方放了一个tab控件,有三个页签。放到设备B上看时tab控件的三个页签被拉得很长,本来放6个页签的空间只放了三个页签。
3. 假设你手上的两个设备,设备A装的是Android2.3,设备B装的是Android4.0,而设备B没有menu建,风格也不一样。你发现两个设备上用同一套风格的皮肤并不合适。
Google提供了一套体系去解决这些问题。我们再回到上面的那张图,drawable文件夹有ldpi、mdpi、hdpi、xhdpi四种。dpi指像素/英寸,而ldpi指120,mdpi指160,hdpi指240,xhdpi指320。小米手机是4英寸、854×480的分辨率,那么小米手机的dpi就是854的平方加480的平方和开2次方后除以4,结果大约是245。如果应用安装在小米手机上,那么系统会调用图中drawable-hdpi里面的资源。这样,你只要做4套资源分别放在drawable-ldpi、drawable-mdpi、drawable-hdpi以及drawable-xdpi下(图标可以按照3:4:6:8的比例制作图片资源),那么就可以解决上面假设1当中提到的问题。
对于相同dpi、但尺寸不一样的设备,可以通过layout文件控制各种资源的布局。Google将设备分为small(2~3英寸)、normal(4英寸左右)、large(5~7英寸)、xlarge(7英寸以上)。在上面的假设2种,我们可以在layout-normal里配置3个页签的tab栏,在layout-xlarge里配置6个页签的tab栏。如果应用在所有设备上布局都一样,那么就不用考虑针对不同尺寸的layout。从图中那些layout*文件夹可以看出,该应用在hdpi及xhdpi上支持横竖屏,而且横竖屏的布局不一致,但没有考虑不同尺寸的设备使用不同布局的情况。
更详细的内容如下
andorid界面单位开发是应该是dip
自适应,包括元素大小自适应,和位置自适应。
元素大小:
图片默认会自适应的。
dip会自适应。
自适应问题。一个公式 px=dip*(density/160);(density/160)在android系统中对应 DisplayMetrics.density在一固定的手机上它是一个常数,0.75,1,1.5。等。有了这个常数用dip做单位在不同手机上就有不同的px了。这就是缩放原理。
drawable-hdpi、drawable-mdpi、drawable-ldpi中的图片是自动选择的。但是如果对应的文件夹下没有所需的图片它会在其他两个文件夹下寻找,找到了按density缩放。
元素坐标:
图片坐标,和触屏事件坐标。 都用相对坐标。
自适应原则:图片缩放自适应,位置用相对位置(单位也用dip)。
所有的机型宽都是相等的dip数,高不一定是相等的dip数。
240x320 density=120 320dipx426.6dip
320x480 density=160 320dipx480dip
480x800 density=240 320dipx533.3dip
480x854 density=240 320dipx569.33dip
我感觉,做手机的屏幕自适应比做web的浏览器兼容更麻烦..以下是搜到的资料,原来android还有这些不为人知的东西:
一:不同的layout
Android手机屏幕大小不一,有480x320, 640x360, 800x480.怎样才能让App自动适应不同的屏幕呢?
其实很简单,只需要在res目录下创建不同的layout文件夹,比如layout-640x360,layout-800x480,所有的layout文件在编译之后都会写入R.java里,而系统会根据屏幕的大小自己选择合适的layout进行使用。
二:hdpi、mdpi、ldpi
在之前的版本中,只有一个drawable,而2.1版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三个,这三个主要是为了支持多分辨率。
drawable- hdpi、drawable- mdpi、drawable-ldpi的区别:
(1)drawable-hdpi里面存放高分辨率的图片,如WVGA (480x800),FWVGA (480x854)
(2)drawable-mdpi里面存放中等分辨率的图片,如HVGA (320x480)
(3)drawable-ldpi里面存放低分辨率的图片,如QVGA (240x320)
系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片。
在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片。
1.术语和概念
术语 |
说明 |
备注 |
Screen size(屏幕尺寸) |
指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸 |
摩托罗拉milestone手机是3.7英寸 |
Aspect Ratio(宽高比率) |
指的是实际的物理尺寸宽高比率,分为long和nolong |
Milestone是16:9,属于long |
Resolution(分辨率) |
和电脑的分辨率概念一样,指手机屏幕纵、横方向像素个数 |
Milestone是854*480 |
DPI(dot per inch) |
每英寸像素数,如120dpi,160dpi等,假设QVGA(320*240)分辨率的屏幕物理尺寸是(2英寸*1.5英寸),dpi=160 |
可以反映屏幕的清晰度,用于缩放UI的 |
Density(密度) |
屏幕里像素值浓度,resolution/Screen size可以反映出手机密度, |
|
Density-independent pixel (dip) |
指的是逻辑密度计算单位,dip和具体像素值的对应公式是dip/pixel=dpi值/160,也就是px = dp * (dpi / 160) |
2. DPI值计算
比如:计算WVGA(800*480)分辨率,3.7英寸的密度DPI,如图1所示
图1
Diagonal pixel表示对角线的像素值(=),DPI=933/3.7=252
3.手机屏幕的分类
3.1根据手机屏幕密度(DPI)或屏幕尺寸大小分为以下3类,如图2所示
图2
3. 2手机屏幕分类和像素密度的对应关系如表1所示:
Low density (120), ldpi |
Medium density (160), mdpi |
High density (240), hdpi |
|
Small screen |
QVGA (240x320) |
||
Normal screen |
WQVGA400 (240x400)WQVGA432 (240x432) |
HVGA (320x480) |
WVGA800 (480x800)WVGA854 (480x854) |
Large screen |
WVGA800* (480x800)WVGA854* (480x854) |
表1
3.3手机尺寸分布情况(http://developer.android.com/resources/dashboard/screens.html)如图3所示,目前主要是以分辨率为800*480和854*480的手机用户居多
图3
从以上的屏幕尺寸分布情况上看,其实手机只要考虑3-4.5寸之间密度为1和1.5的手机
4 UI设计
从开发角度讲,应用程序会根据3类Android手机屏幕提供3套UI布局文件,但是相应界面图标也需要提供3套,如表2所示
Icon Type |
Standard Asset Sizes (in Pixels), for Generalized Screen Densities |
||
Low density screen (ldpi) |
Medium density screen (mdpi) |
High density screen (hdpi) |
|
Launcher |
36 x 36 px |
48 x 48 px |
72 x 72 px |
Menu |
36 x 36 px |
48 x 48 px |
72 x 72 px |
Status Bar |
24 x 24 px |
32 x 32 px |
48 x 48 px |
Tab |
24 x 24 px |
32 x 32 px |
48 x 48 px |
Dialog |
24 x 24 px |
32 x 32 px |
48 x 48 px |
List View |
24 x 24 px |
32 x 32 px |
48 x 48 px |
表2
5 如何做到自适应屏幕大小呢?
1)界面布局方面
需要根据物理尺寸的大小准备5套布局,layout(放一些通用布局xml文件,比如界面中顶部和底部的布局,不会随着屏幕大小变化,类似windos窗口的title bar),layout-small(屏幕尺寸小于3英寸左右的布局),layout-normal(屏幕尺寸小于4.5英寸左右),layout-large(4英寸-7英寸之间),layout-xlarge(7-10英寸之间)
2)图片资源方面
需要根据dpi值准备5套图片资源,drawable,drawalbe-ldpi,drawable-mdpi,drawable-hdpi,drawable-xhdpi
Android有个自动匹配机制去选择对应的布局和图片资源
今天一个开发者问到我为什么游戏开发要删除项目下的hdpi、mdpi和ldpi文件夹;下面详细给大家解答一下:
首先童鞋们如果看过我写的《【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!》这一节的话都应该知道Android从1.6和更高,Google为了方便开发者对于各种分辨率机型的移植而增加了自动适配的功能;
自动适配的原理很简单,只要你建立的项目是1.6或者更高都会看到项目下有drawable-hdpi、drawable-mdpi、drawable-ldpi 三个文件夹,这三个文件夹分别放置高清分辨率、中分辨率、低分辨率的资源文件;那么如果你的项目在高清分辨率上运行的话,系统会默认索引drawable-hdpi文件夹下的资源,其他雷同;
那么既然系统会自动找匹配的文件夹,那么肯定会出现找不到的情况,比如当前你的应用在高清分辨率运行,假设代码中加载一张“himi.png”的图,那么系统首先会去drawable-hdpi文件夹下去找这张图,一旦找不到,系统会再到其他drawable下寻找,再假设你其实把这张“himi.png”放在了drawable-mdpi中,那么系统会默认把这张图片放大;反之一样,如果你在低分辨率中运行加载一张图片的话,一旦你将图片放入高清的drawable-dpi中,那么系统默认缩小这张图;
总结来说:如果你的应用想适配高、中、低分辨率,那么你需要有3套图放入对应的文件夹中,这样系统会智能加载;如果你就想保留一个文件夹,不想让系统智能寻找缩放的话,有两种方式可以解决:
1.删除drawable-hdpi、drawable-mdpi、drawable-ldpi三个文件夹,创建一个drawable文件夹即可;
2.将资源文件放入assets中,因为assets中的资源系统永远不会为其生成id,所以不会智能缩放;
-------------------下面介绍第二点,如何让你的游戏应用高清
其实还是在《【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!》中介绍过,1.6后android有了智能判断的缘故,你获取的屏幕宽高其实是不准确的,详情可以参考【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!》;那么这里要补充一点就是:
如果你在AndroidMainFest 中,定义
这里再补充一下: 一旦你定义了
OK,继续忙了,大家尝试下吧~
1.Screen size 屏幕实际尺寸。
Android讲屏幕实际尺寸分为3个通用的尺寸。
2.Aspect ratio 长宽比
3.Resolution 分辨率
4.Density 密度
5.Density-independent pixel 密度无关的像素
介绍:Adnroid1.6或以上SDK,在AndroidManifest.xml中提供新的一个元素
android:normalScreens="true" 是否支持中屏
android:smallScreens="true" 是否支持小屏
android:anyDensity="true" 是否支持多种不同密度
/>
Android提供3种方式处理屏幕自适应
一.预缩放的资源(基于尺寸和密度去寻找图片)
1.如果找到相应的尺寸和密度,则利用这些图片进行无缩放小时。
2.如果没法找到相应的尺寸,而找到密度,则认为该图片尺寸为 "medium",利用缩放这个图片显示。
3.如果都无法匹配,则使用默认图片进行缩放显示。默认图片默认标配 "medium" (160)。
二.自动缩放的像素尺寸和坐标(密度兼容)
1.如果应用程序不支持不同密度android:anyDensity="false",系统自动缩放图片尺寸和这个图片的坐标。
(代码中体现)
2.对于预缩放的资源,当android:anyDensity="false",也不生效。
3.android:anyDensity="false",只对密度兼容起作用,尺寸兼容没效果
三.兼容模式显示在大屏幕,尺寸(尺寸兼容)
1.对于你在
2. 对于你在
密度独立:
系统默认应用支持DIP单位的,三个使用DIP的地方:
1.加载资源时,使用DIP实现预缩放的资源。
2.在Layout使用DIP,系统自动完成缩放。
3.在应用程序中,自动缩放一些绝对像素。
(只有在android:anyDensity="false"生效)即屏幕自适应方式二
4.像素单位都使用DIP,文本单位使用SP
最佳屏幕独立实践:
1.使用wrap_content, fill_parent 和使用dip作为像素单位in XML layout files。
2.避免使用AbsoluteLayout
3.在代码中,不要使用像素数字硬编码,而是要通过dip转换为px。
例子:
你使用手势分析器分析一个scroll手势,假如,你滚动的距离是16px。
1.在一个160dip的屏幕中,你实际移动距离 16px / 160dpi = 1/10th of an inch (or 2.5 mm)
2.在一个240dip的屏幕中,你实际移动距离 16px / 240dpi = 1/15th of an inch (or 1.7 mm)
// The gesture threshold expressed in dip
private static final float GESTURE_THRESHOLD_DIP = 16.0f;
// Convert the dips to pixels
final float scale = getContext().getResources().getDisplayMetrics().density;
mGestureThreshold = (int) (GESTURE_THRESHOLD_DIP * scale);
4.使用密度和/或尺寸特定资源(通过文件夹)
关于预缩放或者自动缩放图片或9格图
1.系统是一定对会资源包下的图片进行合理的缩放。
例如:一张240x240高密度图片,显示在中密度的屏幕上,图片大小自动变为160x160。
2.你在API中不会得到被缩放后的图片尺寸,得到还是你原来图片的尺寸。
3.如果你不想系统自动帮你缩放图片,可以建立一个res/drawable-nodpi文件夹,存放你的图片。
4.也可以通过BitmapFactory.Options 完成系统自动缩放图片或9格图(在画图时)。
5.自动缩放图片比预缩放花费更多CPU,但是用更少内存(RAM or ROM ?)
一、相关概念
a) android支持density的版本
Android从1.6版本开始支持density(对应API Level 4)
b) density
density值表示每英寸有多少个显示点,比如240就是每英寸240个点,它是针对设备的属性,它是屏幕物理长宽的扩展,给屏幕设置为低密度显示的内容少,同样的条件下,密度小的屏幕显示同样的按钮看起来大,高密度的看起来小
c) 分辨率
是整个屏是多少点,比如800x480,它是对于软件来说的显示单位,以px为单位的点
d) 设置density的效果
不同density下屏幕分辨率信息,以480dip*800dip的WVGA为例
density=120时 屏幕实际分辨率为240px*400px
density=160时 屏幕实际分辨率为320px*533px
density=240时 屏幕实际分辨率为480px*800px
二、相关代码及设置
a) AndroidManifest.xml
< supports-screens android:anyDensity="true"/>
< uses-sdk android:minSdkVersion="4">
b) 资源目录名(android 2.0以后)
res/xxx-hdpi 当density为240时,使用此目录下的资源
res/xxx-mdpi 当density为160时,使用此目录下的资源
res/xxx-ldpi 当density为120时,使用此目录下的资源
res/xxx 不常后缀,为默认设置,同xxx-mdpi
如果硬件相应的desity的目录不存在,系统会利用存在的density自动乘以系数计算出相应的density
c) 资源单位(layout xml文件中定义大小的单位)
i. dp=dip=dx (Density independent pixel)
基于屏幕密度的抽象单位,布局时尽量使用单位dip,少使用px
dip是应用用于定义UI的虚拟单位,用于说明与密度无关的尺寸和位置。
dip点等价于160dpi密度中的一个物理点,密度由平台决定,换算公式如下pixels = dips * (density / 160)
,
160DPI的密度系数是1例如 240 dpi的屏幕,1个dip点等于1.5个物理点
ii. px
设置的绝对点数, 如果使用更高density的系统, 控件就会变小
三、设置density
设置系统变量hw.lcd.density,可设置density
四、实现density的关键源码
a) BitmapFactory.java
b) ComptibilityInfo.java