前言:
这篇文章内容主要来自于Google的官方指导,有些地方加入笔者自己的理解。
有很多内容现在应用的已经很少了,比如ldpi屏幕。现在android设备的分辨率越来越高,尺寸也接近于同一,适配难度也低了很多。
Google在过去不断推出了新的View组件、容器组件。个人建议尽量使用新的组件来实现UI,它们往往性能更好,风格和样式趋近于MD的统一风格,而且更易于适配不同的屏幕。
但笔者仍然保留了低像素屏幕适配的部分。因为一些性能不高的设备上,仍可能在使用像素和密度较低的屏幕。
一. 屏幕支持概览
1.1 术语和概念
屏幕尺寸
按屏幕对角测量的实际物理尺寸。
为简便起见,Android 将所有实际屏幕尺寸分组为四种通用尺寸:小、 正常、大和超大。屏幕密度
屏幕物理区域中的像素量;通常称为 dpi(每英寸 点数)。例如, 与“正常”或“高”密度屏幕相比,“低”密度屏幕在给定物理区域的像素较少。
为简便起见,Android 将所有屏幕密度分组为六种通用密度: 低、中、高、超高、超超高和超超超高。方向
从用户视角看屏幕的方向,即横屏还是 竖屏,分别表示屏幕的纵横比是宽还是高。请注意, 不仅不同的设备默认以不同的方向操作,而且 方向在运行时可随着用户旋转设备而改变。- 分辨率
屏幕上物理像素的总数。添加对多种屏幕的支持时, 应用不会直接使用分辨率;而只应关注通用尺寸和密度组指定的屏幕 尺寸及密度。 - 密度无关像素 (dp)
在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置。
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
1.2 支持的屏幕范围
- 四种通用尺寸:小、正常、 大 和超大
六种通用的密度:
- ldpi(低)~120dpi
- mdpi(中)~160dpi
- hdpi(高)~240dpi
- xhdpi(超高)~320dpi
- xxhdpi(超超高)~480dpi
- xxxhdpi(超超超高)~640dpi
密度独立性
- 应用显示在密度不同的屏幕上时,如果它保持用户界面元素的物理尺寸(从 用户的视角),便可实现“密度独立性” 。
保持密度独立性很重要,因为如果没有此功能,UI 元素(例如 按钮)在低密度屏幕上看起来较大,在高密度屏幕上看起来较小。这些 密度相关的大小变化可能给应用布局和易用性带来问题。
上图表示:不支持不同密度的示例应用在低、中、高密度屏幕上的显示情况。
上图表示:良好支持不同密度(密度独立)的示例应用在低、中、高密度屏幕上的显示情况。- Android 系统可帮助您的应用以两种方式实现密度独立性:
- 系统根据当前屏幕密度扩展 dp 单位数
- 系统在必要时可根据当前屏幕 密度将可绘制对象资源扩展到适当的大小
二. 如何支持多种屏幕
Android 支持多种屏幕的基础是它能够管理针对当前屏幕配置 以适当方式渲染应用的布局和位图 可绘制对象。
系统可处理大多数工作,通过适当地 缩放布局以适应屏幕尺寸/密度和根据屏幕密度缩放位图可绘制对象 ,在每种屏幕配置中渲染您的应用。
2.1 通常做法
- 在清单中显式声明您的应用 支持哪些屏幕尺寸:
<supports-screens>
- 为不同屏幕尺寸提供不同的布局
- 可用于提供尺寸特定资源的配置限定符包括 small、normal、large 和 xlarge。例如,超大屏幕的布局应使用
layout-xlarge/
。 - 从 Android 3.2(API 级别 13)开始,以上尺寸组已弃用,您 应改为使用
sw<N>dp
配置限定符来定义布局资源可用的最小宽度。例如,如果多窗格平板电脑布局需要至少 600dp 的屏幕宽度,应将其放在layout-sw600dp/
中。
- 可用于提供尺寸特定资源的配置限定符包括 small、normal、large 和 xlarge。例如,超大屏幕的布局应使用
- 为不同屏幕密度提供不同的位图可绘制对象
- 可用于密度特定资源的配置限定符(在下面详述) 包括 ldpi(低)、mdpi(中)、 hdpi(高)、xhdpi(超高)、xxhdpi (超超高)和 xxxhdpi(超超超高)。
2.2 使用配置限定符
在项目的 res/ 目录中新建一个目录,并使用以下 格式命名: -
- 是标准资源名称(例如 drawable 或 layout)。
- 是下表 1 中的配置限定符,用于指定要使用这些资源的屏幕配置(例如 hdpi 或 xlarge)。
您可以一次使用多个 — 只需使用短划线分隔每个 限定符。
将适当的配置特定资源保存在此新目录下。这些资源 文件的名称必须与默认资源文件完全一样。
示例:
res/layout/my_layout.xml // layout for normal screen size ("default") res/layout-large/my_layout.xml // layout for large screen size res/layout-xlarge/my_layout.xml // layout for extra-large screen size res/layout-xlarge-land/my_layout.xml // layout for extra-large in landscape orientation res/drawable-mdpi/graphic.png // bitmap for medium-density res/drawable-hdpi/graphic.png // bitmap for high-density res/drawable-xhdpi/graphic.png // bitmap for extra-high-density res/drawable-xxhdpi/graphic.png // bitmap for extra-extra-high-density res/mipmap-mdpi/my_icon.png // launcher icon for medium-density res/mipmap-hdpi/my_icon.png // launcher icon for high-density res/mipmap-xhdpi/my_icon.png // launcher icon for extra-high-density res/mipmap-xxhdpi/my_icon.png // launcher icon for extra-extra-high-density res/mipmap-xxxhdpi/my_icon.png // launcher icon for extra-extra-extra-high-density
2.2.3 设计替代布局和可绘制对象
替代布局
一般而言,在不同的屏幕配置上测试应用后,您会知道是否需要用于不同屏幕尺寸的替代布局。例如:
- 在小屏幕上测试时,可能会发现您的布局不太适合 屏幕。例如,小屏幕设备的屏幕宽度可能无法容纳一排 按钮。在此情况下,您应该为小屏幕提供调整 按钮大小或位置的替代布局。
在超大屏幕上测试时,可能会发现您的布局无法 有效地利用大屏幕,并且明显拉伸填满屏幕。
在此情况下,您应该为超大屏幕提供替代布局,以提供 针对大屏幕(例如平板电脑)优化、重新设计的 UI。
虽然您的应用不使用替代布局也能在大屏幕上正常运行,但 必须让用户感觉您的应用看起来像是专为其 设备而设计。如果 UI 明显拉伸,用户很可能对 应用体验不满意。
- 对比横屏测试和竖屏测试时 可能会发现,竖屏时置于底部的 UI 在横屏时应位于屏幕右侧。
简而言之,您应确保应用布局:
- 适应小屏幕(让用户能实际使用您的应用)
- 已针对大屏幕优化,可以利用其他屏幕空间
- 已同时针对横屏和竖屏方向优化
替代可绘制对象
要为不同的密度创建替代位图可绘制对象,应遵循六种通用密度之间的 3:4:6:8:12:16 缩放比率。例如,如果您的 位图可绘制对象是对中密度屏幕使用 48x48 像素,则所有不同的尺寸应为:
- 36x36 (0.75x) 用于低密度
- 48x48(1.0x 基线)用于中密度
- 72x72 (1.5x) 用于高密度
- 96x96 (2.0x) 用于超高密度
- 144x144 (3.0x) 用于超超高密度
- 192x192 (4.0x) 用于超超超高密度(仅限启动器图标)
三. 声明适用于 Android 3.2 的平板电脑布局
3.1 通常做法:
- 对于第一代运行 Android 3.0 的平板电脑,声明平板电脑 的正确方式是将它们放在有 xlarge 配置限定符的目录(例如 res/layout-xlarge/)中。
- 现在,应当选择一个宽度并将其设计为最小尺寸,或者在布局设计完成后测试 其支持的最小宽度。
3.2 使用新尺寸限定符(3.2中引入)
- smallestWidth:swdp
- 例如sw600dp,如果布局要求屏幕区域的最小尺寸始终至少为 600 dp,则可使用此限定符创建布局资源 res/layout-sw600dp/。
- smallestWidth 是设备的固定屏幕尺寸特性;设备的 smallestWidth 不会随屏幕方向的变化而改变。
- 可用屏幕宽度: wdp
- 指定资源应该使用的最小可用宽度(dp 单位) — 由 值定义。
- 当屏幕的方向在横屏与竖屏之间切换时,系统对应的 宽度值将会变化,以 反映 UI 可用的当前实际宽度。
- 可用屏幕高度: hdp,特性与可用屏幕宽度类似。
3.3 配置示例:
- 下面提供了一些常见的屏幕宽度值:
- 320dp:常见手机屏幕(240x320 ldpi、320x480 mdpi、480x800 hdpi 等)。
- 480dp:中间平板电脑,例如 Streak (480x800 mdpi)。
- 600dp:7 英寸平板电脑 (600x1024 mdpi)。
- 720dp:10 英寸平板电脑(720x1280 mdpi、800x1280 mdpi 等)。
示例:如果 600dp 是平板电脑布局支持的最小可用宽度,可以提供以下两 组布局:
res/layout/main_activity.xml # 手机 res/layout-sw600dp/main_activity.xml # 平板
在此情况下,可用屏幕空间的最小宽度必须是 600dp,才可 应用平板电脑布局。
示例: 对于要进一步自定义 UI 以区分不同尺寸(例如 7 英寸和 10 英寸平板电脑)的其他情况,可以定义其他最小宽度布局:
res/layout/main_activity.xml # 手机 res/layout-sw600dp/main_activity.xml # 7寸平板,最小600dp宽 res/layout-sw720dp/main_activity.xml # 10寸平板 最小700dp宽
使用“最小宽度”限定符
sw<N>dp
,用于指定屏幕两边的最小值,而不管设备当前的方向如何。使用
sw<N>dp
是指定布局可用于整体屏幕尺寸的简便方法,它会忽略屏幕的方向。有时必须确定布局当前可用的精确宽度或高度。比如屏幕只要600dp的宽度时,就并排显示两个片段的双窗格布局,无论横屏还是竖屏。此时,可以使用下面的配置:
res/layout/main_activity.xml # 手机 res/layout-w600dp/main_activity.xml # 多窗格,只要屏幕宽度达到600dp(无论横屏竖屏)
3.3 声明屏幕尺寸支持
<supports-screens>
的一些属性:
1. android:requiresSmallestWidthDp
:指定需要的最小宽度或高度
- 示例,如果应用只用于最小可用宽度为 600dp 的平板电脑样式设备:
```
<manifest ... >
<supports-screens android:requiresSmallestWidthDp="600" />
...
</manifest>
```
2. android:compatibleWidthLimitDp
:指定用户支持的最大“最小宽度”,将屏幕兼容性模式用作用户可选的功能。
- 如果设备可用屏幕的最小宽度大于这个值,用户仍可安装应用,但提议在屏幕兼容性模式下运行。
- 默认情况下,屏幕兼容性模式会停用,并且布局照例会调整大小以适应屏幕,但按钮会显示在系统栏中,可让用户打开和关闭屏幕兼容模式。
- 如果应用可针对大屏幕正确调整大小,则无需使用此属性。建议不要使用此属性,而是确保布局针对较大屏幕调整大小。
3. android:largestWidthLimitDp
:指定应用支持的最大“最小宽度”来强制启用屏幕兼容性模式。
- 如果设备可用屏幕的最小宽度大于这个值,应用将在屏幕兼容性模式下运行,且用户无法停用该模式。
四. 最佳做法
对布局尺寸使用
wrap_content
、match_parent
或 dp 单位- 同时选择 sp(缩放独立的像素)来定义Text大小
不要在代码中使用硬编码的像素值
- 如果要在代码中使用尺寸数值,请将px转换为dp
- 不要使用 AbsoluteLayout, 这个布局早已被弃用
使用尺寸和密度特定资源
res/drawable-mdpi/icon.png //中等密度屏幕的icon,100px的图片 res/drawable-hdpi/icon.png //高密度屏幕的icon,150px的图片
五. 其他密度注意事项
5.1 资源(例如图片)的预缩放
系统预缩放机制:
- 根据当前屏幕的密度,系统将使用App提供的任何尺寸或密度特定资源,并且不加缩放而显示它们。
- 如果没有可用于正确密度的资源,系统将加载默认资源,并按需要向上或向下扩展,以匹配当前屏幕的密度。
- 系统假设默认资源(没有配置限定符的目录中的资源)针对基线屏幕密度 (mdpi) 而设计, 除非它们加载自密度特定的资源目录。
- 因此,系统会执行预缩放,以将位图调整至适应当前屏幕密度的大小。
预缩放机制的一些问题:
如果请求预缩放的资源的尺寸,系统将返回缩放后尺寸的值。
例如,针对 mdpi 屏幕以 50x50 像素 设计的位图在 hdpi 屏幕上将扩展至 75x75 像素(如果没有 用于 hdpi 的备用资源),并且系统会这样报告大小。
避免预缩放:
如果不希望系统预缩放资源,可以将资源放在res/drawable-nodpi/
中:res/drawable-nodpi/icon.png
当系统使用icon.png 位图时,不会根据当前设备密度缩放。
5.2 像素尺寸和坐标的自动缩放
- 停用预缩放的两个办法:
- 在
<supports-screens>
中奖android:anyDensity
设置为false
- 在使用
BitmapFactory
加载图片时,将inScaled
设置为false
,即可对图片停用预缩放,启用自动缩放。
- 在
- 自动缩放:在停用预缩放的情况下,系统在绘制时会自动缩放任何绝对的像素坐标和像素尺寸值,缩放的标准则是mdpi。
- 这样做的目的是确保用像素定义的屏幕元素仍以它们在基线屏幕密度 (mdpi) 下的大致相同物理尺寸显示。
- 系统会自动处理此缩放,并且向应用报告缩放后的像素尺寸,而不是物理像素尺寸。
- 例如,有一块480x800的hdpi屏幕,如果应用停用了预缩放,那么在查询屏幕尺寸时,会报告320x533像素!因为系统会自动将像素缩放为mdpi密度的近似尺寸。
- 通常,不应当停用预缩放。
5.3 缩放运行时创建的bitmap
对
Bitmap
调用setDensity()
来指定密度,参数从DisplayMetrics
选择密度常量。如果bitmap不指定密度,系统会对bitmap应用“自动缩放”:
系统加载bitmap时,默认情况下假设位图是针对基线中密度(mdpi)屏幕而设计,然后在绘制时自动缩放位图。
- 使用
BitmapFactory.Options
来定义bitmap的属性,来确定系统是否要缩放或者如何缩放。
- 如果
inScaled
设置为false
,系统不会进行预缩放,而在绘制时使用自动缩放。
- 如果
5.4 将 dp 单位转换为px单位
我们永远不推荐在代码中使用px值。如有需要,请将px转换为dp。
// 手势移动像素数 private static final float GESTURE_THRESHOLD_DP = 16.0f; // 获取屏幕密度比值 final float scale = getResources().getDisplayMetrics().density; // dp转px mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);
转换时加上 0.5f,将该数字四舍五入到最接近的整数。
DisplayMetrics.density
根据当前屏幕密度指定dp转px使用的缩放系数。- 在ldpi上,等于0.75
- 在mdpi上,等于1.0
- 在hdpi上,等于1.5
- 在xhdpi上,等于2.0
使用预缩放的配置值
使用
ViewConfiguration
类访问 Android 系统使用的通常距离、 速度和时间。比如,getScaledTouchSlop()
可获取框架用作滚动阈值的距离(像素):private static final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();
六. 测试:在多个屏幕上测试应用
使用AVD Manager创建各种尺寸、像素、屏幕密度、Android版本的模拟器,还有很多设备特性可以自定义。
也可以使用Genymotion来创建模拟器。