Android 屏幕适应

基础知识:

屏幕密度:

Density-independent pixel (dp):密度无关像素单位(一个相对的值)。1dp 的大小相当于一个 160 dpi 屏幕上一个像素的大小。

计算方法:px = dp * (dpi / 160)。这个公式说明,比如一个10寸屏幕分辨率为 1280*800,另一个10寸屏分辨率为 2560*1600。比如在 layout 文件中有一个图片大小为 60dp。那么根据公式,两者显示效果大体是一样的,区别是图片清晰度不同。

也就是说,安卓设备的屏幕以屏幕密度大小来划分,界面设计使用与屏幕密度无关的 dp 为单位,这样可以提高不同屏幕密度下的兼容性。

dpi (dots per inch):每寸像素数。分为 ldpi 低(~120dpi),mdpi 中(~160dpi),hdpi 高(~240dpi),xhdpi 超高(~320dpi) ,nodpi 密度无关资源:不会对资源根据屏幕密度缩放。

160 dpi 的大小相当于:(320*240)分辨率,物理尺寸是(2英寸*1.5英寸)的屏幕。

比如两个1280*800的屏幕,尺寸分别为10寸和5寸。在一个 layout 上有一个 640dp*400dp 的图片。由于小屏幕的 dpi 是大屏幕的二倍,所以对于同样 dp 的图片,在小屏幕上显示用的像素是大屏幕的二倍。而两者像素数相同,所以显示出来的效果是小屏幕上会显示出一个是大屏幕上二倍大的图片(相对大小)。绝对大小他们是一样的。所以屏幕适配时必须要看屏幕密度,屏幕密度高的需要调小 dp 值以保证有同样的显示效果。

所以,屏幕适配的原理是:

对 layout 要用屏幕大小来划分:

  • layout-xlarge screens are at least 960dp x 720dp
  • layout-large screens are at least 640dp x 480dp
  • layout-normal screens are at least 470dp x 320dp
  • layout-small screens are at least 426dp x 320dp

对 图片 要用屏幕密度 dpi 来划分:

  • drawable-ldpi (low)
  • drawable-mdpi (medium)
  • drawable-hdpi (high)
  • drawable-xhdpi (extra high)

常见 Android 设备尺寸(dp):

https://design.google.com/devices/

字体大小的适配:

和 dp 的理论一样,字体大小使用同样密度无关的单位 sp。用法也与 dp 一样。

在代码中,想要使用 dimen 里面设置的 sp 字体大小应该这样做:

textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.textSize1));

而不能这样:

textView.setTextSize(getResources().getDimension(R.dimen.textSize1));

因为如果不明确是 TypedValue.COMPLEX_UNIT_PX 的话,系统会自动根据屏幕密度来返回一个像素值。注意要用 TypedValue.COMPLEX_UNIT_PX 而不是 TypedValue.COMPLEX_UNIT_SP

****************************************************************************************************************

实践:

1. 屏幕对多分辨率适应的最好方法可能就是用 weight 属性了。

线型布局里可以使用 layout_weight 属性来让控件按比例显示。这样不同屏幕分辨率下会有一致的显示效果。

这个属性有两种使用方法:

  1. 把 layout_width 或 layout_height (取决于你想在长度上还是宽度上按比例显示) 的值设为 match_parent 。

  这种情况下,weight 值越小,占的地方越大。这种方法缺点是比例需要自己计算,因为它是反的。

  2. 把 layout_width 或 layout_height (取决于你想在长度上还是宽度上按比例显示) 的值设为 0 px 。

  这种情况下,weight 值越大,占的地方越大。

但是 weight 属性对相对布局是无效的。所以在使用时需要注意。

************************************************************************************

2. 也可能用代码获取屏幕分辨率的方式布局。优点是准确,缺点是麻烦。因为在所有控件的大小都需要在代码里重新设置,非常麻烦。这种方法不适合项目里有很多 layout 的情况。

3. 另外也可以为每个分辨率单独设计一个布局。缺点还是麻烦,因为分辨率有那么多种。优点是还是准确。

4:3 平板屏幕适配:

对于安卓平板,标准屏幕比例是 16:9。所以对于平板应用,平常只对这个比例支持。如果想支持 4:3 的平板,需要对 layout 里的各控件的大小进行重新计算,并且使用 4:3 比例的背景图片。

那么问题来了,如果根据平板尺寸判断屏幕比例并适配呢?这个问题比较复杂,我做了很多尝试。

res 文件夹下存放所有资源,包括 values,layout,drawables 等。values 和 layout 常用 sw(最短宽度)这个参数,而 drawable 常用屏幕密度来划分。很明显这些都不适用于这个情况。

有一个参数 long/notlong,表示屏幕是否是长屏幕。什么样算长?我没有具体的数据,但在测试的时候,一个 1280*800 物理按键的10寸屏幕不是长屏幕。而如果同样分辨率和尺寸的没有物理按键的屏幕却是长屏幕。一个 1024*768 的屏幕当然一定不是长屏幕。所以除非是没有物理按键的 16:9 平板,否则无法通过这个参数来区分 16:9 还是 4:3。

解决办法是什么呢,测试中发现的解决办法是用屏幕尺寸区分。w1024dp 这个参数可以区分出一个宽度为 1024dp 的屏幕。以三星TAB A为例,9.6寸,1024*768,1024dp*768dp。这个参数对它适用。但是如果一个平板尺寸为 1024dp*600dp,那就得着急了。另外如果世界上存在小尺寸的 4:3 平板,那屏幕尺寸就很可能不是 1024dp*768dp,那么这个适配方法也会失效。所以真正一劳永逸的解决办法还在寻找中。

有一篇文章可供参考:

http://www.eoeandroid.com/thread-173973-1-1.html

最近找到了最佳解决办法:

其实早就知道 9-patch (.9) image。只是最近才测试成功。

思路是这样的,9-patch 中分为“横向可拉伸”,“纵向可拉伸”,“四周都可拉伸”,“不拉伸”四个区域。 背景图中的不想拉伸的部分,放在“不拉伸”区域。其他的就很随意了,随便它怎么拉。“不拉伸”区域在四个角上,所以即使图片中包含多个比较分散的不想被拉伸的部分也没有问题。

使用 9-patch 可以进一步压缩图片尺寸,可拉伸的部分可以省掉。真的是一举多得。

程序中使用 9-patch 的注意事项:

1. 要使用 ImageView,而且要用做 src 而不是 background。

2. 要使用 scaleType = “centerCrop”

  如果使用 center,不拉伸,保持图片比例,图片尺寸和 ImageView 不符合的时候无法填满。

  默认是 fitCenter,保持图片比例,拉伸,但和ImageView 不符合的时候无法填满。

  其他的也都测试过都不符合。

  centerCrop 是保持图片比例并拉伸,另外一个特性是它会根据 ImageView 的宽度来充满。

制作工具:

android studio,9-patch generator 等。

参考文章:

http://www.cnblogs.com/vanezkw/archive/2012/07/19/2599092.html

今天测试中发现,或许起作用的并不是 9-Patch。而是 ImageView 的 centerCrop 属性。所以对于这个问题,其实这一个参数就能解决。

上一篇:React 表单控件onSubmit


下一篇:sfidsk创建可启动分区问题