前言
本文内容来自 Google 开发者平台上的 Android 指南
提供资源 | Android Developer
提供资源
应该始终外部化应用资源,例如图像和代码中的字符串,这样有利于单独维护这些资源。此外,还应该为特定设备配置提供备用资源,方法是将它们分组到专门命名的资源目录中。 在运行时,Android 会根据当前配置使用适当的资源。例如,您可能需要根据屏幕尺寸提供不同的 UI 布局,或者根据语言设置提供不同的字符串。
外部化应用资源后,可使用 R 类中生成的资源 ID 访问这些资源。
分组资源类型
表 1. 项目 res/ 目录内支持的资源目录。
注意:切勿将资源文件直接保存在 res/ 目录内,这会导致出现编译错误。
保存在表 1 中定义的子目录下的资源是“默认”资源。即这些资源定义应用的默认设计和内容。但是,采用 Android 技术的不同设备类型可能需要不同类型的资源。例如,如果设备的屏幕尺寸大于标准屏幕,则应提供不同的布局资源,以充分利用额外的屏幕空间。 或者,如果设备的语言设置不同,则应提供不同的字符串资源,以转换用户界面中的文本。 要为不同的设备配置提供这些不同资源,除了默认资源以外,您还需要提供备用资源。
提供备用资源
几乎每个应用都应提供备用资源以支持特定的设备配置。 例如,对于不同的屏幕密度和语言,应分别包括备用可绘制对象资源和备用字符串资源。 在运行时,Android 会检测当前设备配置并为应用加载合适的资源。
为一组资源指定特定于配置的备用资源:
1. 在 res/ 中创建一个以 -
形式命名的新目录。
是相应默认资源的目录名称(如表 1 中所定义)。 是指定要使用这些资源的各个配置的名称(如表 2 中所定义)。可以追加多个 。以短划线将其分隔。
注意:追加多个限定符时,必须按照表 2 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。
2. 将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。
例如,以下是一些默认资源和备用资源:
res/
drawable/
icon.png
background.png
drawable-hdpi/
icon.png
background.png
hdpi 限定符表示该目录中的资源适用于屏幕密度较高的设备。其中每个可绘制对象目录中的图像已针对特定的屏幕密度调整大小,但是文件名完全相同。 这样一来,用于引用 icon.png 或 background.png 图像的资源 ID 始终相同,但是 Android 会通过将设备配置信息与资源目录名称中的限定符进行比较,选择最符合当前设备的各个资源版本。
表 2 按优先顺序列出了有效的配置限定符;如果对资源目录使用多个限定符,则必须按照表中列出的顺序将它们添加到目录名称。
表 2. 配置限定符名称。
注:有些配置限定符是从 Android 1.0 才开始添加,因此并非所有版本的 Android 系统都支持所有限定符。使用新限定符会隐式添加平台版本限定符,因此较旧版本系统的设备必然会忽略它。 例如,使用 w600dp 限定符会自动包括 v13 限定符,因为可用宽度限定符是 API 级别 13 中的新增配置。为了避免出现任何问题,请始终包含一组默认资源(一组“不带限定符”的资源)。
限定符命名规则
以下是一些关于使用配置限定符名称的规则:
可以为单组资源指定多个限定符,并使用短划线分隔。例如,drawable-en-rUS-land 适用于横排美国英语设备。
-
这些限定符必须遵循表 2 中列出的顺序。例如:
- 错误:drawable-hdpi-port/
- 正确:drawable-port-hdpi/
不能嵌套备用资源目录。例如,您不能拥有 res/drawable/drawable-en/。
值不区分大小写。在处理之前,资源编译器会将目录名称转换为小写,以避免不区分大小写的文件系统出现问题。名称中使用的任何大写字母只是为了便于认读。
对于每种限定符类型,仅支持一个值。例如,若要对西班牙语和法语使用相同的可绘制对象文件,则您肯定不能拥有名为 drawable-rES-rFR/ 的目录,而是需要两个包含相应文件的资源目录,如 drawable-rES/ 和 drawable-rFR/。然而,实际上您无需将相同的文件都复制到这两个位置。相反,您可以创建指向资源的别名。
将备用资源保存到以这些限定符命名的目录中之后,Android 会根据当前设备配置在应用中自动应用这些资源。 每次请求资源时,Android 都会检查备用资源目录是否包含所请求的资源文件,然后查找最佳匹配资源。 如果没有与特定设备配置匹配的备用资源,则 Android 会使用相应的默认资源(一组用于不含配置限定符的特定资源类型的资源)。
创建别名资源
如果想将某一资源用于多种设备配置(但是不想作为默认资源提供),则无需将同一资源放入多个备用资源目录中。 相反,您可以(在某些情况下)创建备用资源,充当保存在默认资源目录下的资源的别名。
注:并非所有资源都会提供相应机制让您创建指向其他资源的别名。 特别是,xml/ 目录中的动画资源、菜单资源、原始资源以及其他未指定资源均不提供此功能。
例如,假设您有一个应用图标 icon.png,并且需要不同语言区域的独特版本。 但是,加拿大英语和加拿大法语这两种语言区域需要使用同一版本。 您可能会认为需要将相同的图像复制到加拿大英语和加拿大法语对应的资源目录中,但事实并非如此。 相反,您可以将用于二者的图像另存为 icon_ca.png(除 icon.png 以外的任何名称),并将其放入默认 res/drawable/ 目录中。然后,在 res/drawable-en-rCA/ 和 res/drawable-fr-rCA/ 中创建 icon.xml 文件,使用
可绘制对象
要创建指向现有可绘制对象的别名,请使用
如果将此文件另存为 icon.xml(例如,在备用资源目录中,另存为 res/drawable-en-rCA/),则会编译到可作为 R.drawable.icon 引用的资源中,但实际上它是 R.drawable.icon_ca 资源(保存在 res/drawable/ 中)的别名。
布局
要创建指向现有布局的别名,请使用包装在
如果将此文件另存为 main.xml,则会编译到可作为 R.layout.main 引用的资源中,但实际上它是 R.layout.main_ltr 资源的别名。
字符串和其他简单值
要创建指向现有字符串的别名,只需将所需字符串的资源 ID 用作新字符串的值即可。例如:
Hello
@string/hello
R.string.hi 资源现在是 R.string.hello 的别名。
其他简单值的原理相同。 例如,颜色:
#f00
@color/red
利用资源提供最佳设备兼容性
要使应用支持多种设备配置,则务必为应用使用的每种资源类型提供默认资源,这一点非常重要。
例如,如果应用支持多种语言,请始终包含不带语言和区域限定符的 values/ 目录(用于保存字符串)。相反,如果您将所有字符串放入带有语言和区域限定符的目录中,则在语言设置不支持您的字符串的设备上运行应用时,应用将会崩溃。 但是,只要提供默认 values/ 资源,应用就会正常运行(即使用户不理解该语言,这也总比崩溃要好)。
同样,如果根据屏幕方向提供不同的布局资源,则应选择一个方向作为默认方向。 例如,不要在 layout-land/ 和 layout-port/ 中分别提供横向和纵向的布局资源,而是保留其中之一作为默认设置,例如:layout/ 用于横向,layout-port/ 用于纵向。
提供默认资源至关重要,这不仅仅因为应用可能在超出预期的配置上运行,也因为新版 Android 有时会添加旧版本不支持的配置限定符。若要使用新的资源限定符,又希望维持对旧版 Android 的代码兼容性,则当旧版 Android 运行应用时,如果不提供默认资源,应用将会崩溃,这是因为它无法使用以新限定符命名的资源。例如,如果将 minSdkVersion 设置为 4,并使用夜间模式(night 或 notnight,API 级别 8 中新增配置)限定所有可绘制对象资源,则 API 级别 4 设备无法访问可绘制对象资源,而且会崩溃。在这种情况下,您可能希望 notnight 成为默认资源,为此,您应排除该限定符,使可绘制对象资源位于 drawable/ 或 drawable-night/ 中。
因此,为了提供最佳设备兼容性,请始终为应用正确运行所必需的资源提供默认资源。 然后,使用配置限定符为特定的设备配置创建备用资源。
这条规则有一个例外:如果应用的 minSdkVersion 为 4 或更高版本,则在提供带屏幕密度限定符的备用可绘制对象资源时,不需要默认可绘制对象资源。 即使没有默认可绘制对象资源,Android 也可以从备用屏幕密度中找到最佳匹配项并根据需要缩放位图。 但是,为了在所有类型的设备上提供最佳体验,您应该为所有三种类型的密度提供备用可绘制对象。
Android 如何查找最佳匹配资源
当您请求要为其提供备用资源的资源时,Android 会根据当前的设备配置选择要在运行时使用的备用资源。为演示 Android 如何选择备用资源,假设以下可绘制对象目录分别包含相同图像的不同版本:
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
同时,假设设备配置如下:
语言区域 = en-GB
屏幕方向 = port
屏幕像素密度 = hdpi
触摸屏类型 = notouch
主要文本输入法 = 12key
通过将设备配置与可用的备用资源进行比较,Android 从 drawable-en-port
中选择可绘制对象。
系统使用以下逻辑决定要使用的资源:
1. 淘汰与设备配置冲突的资源文件。
drawable-fr-rCA/ 目录与 en-GB 语言区域冲突,因而被淘汰。
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
例外:屏幕像素密度是唯一一个未因冲突而被淘汰的限定符。 尽管设备的屏幕密度为 hdpi,但是 drawable-port-ldpi/ 未被淘汰,因为此时每个屏幕密度均视为匹配。
2. 选择列表(表 2)中(下一个)优先级最高的限定符。(先从 MCC 开始,然后下移。)
3. 是否有资源目录包括此限定符?
若无,请返回到第 2 步,看看下一个限定符。(在该示例中,除非达到语言限定符,否则答案始终为“否”。)
若有,请继续执行第 4 步。
4. 淘汰不含此限定符的资源目录。在该示例中,系统会淘汰所有不含语言限定符的目录。
drawable/
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
例外:如果涉及的限定符是屏幕像素密度,则 Android 会选择最接近设备屏幕密度的选项。通常,Android 倾向于缩小大型原始图像,而不是放大小型原始图像。
5. 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止。在此示例中,屏幕方向是下一个判断是否匹配的限定符。因此,未指定屏幕方向的资源被淘汰:
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
剩下的目录是 drawable-en-port
。
尽管对所请求的每个资源均执行此程序,但是系统仍会对某些方面做进一步优化。例如,系统一旦知道设备配置,即会淘汰可能永远无法匹配的备用资源。比如说,如果配置语言是英语(“en”),则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中(不过,仍会将不带语言限定符的资源目录包含在该池中)。
根据屏幕尺寸限定符选择资源时,如果没有更好的匹配资源,则系统将使用专为小于当前屏幕的屏幕而设计的资源(例如,如有必要,大尺寸屏幕将使用标准尺寸的屏幕资源)。 但是,如果唯一可用的资源大于当前屏幕,则系统不会使用这些资源,并且如果没有其他资源与设备配置匹配,应用将会崩溃(例如,如果所有布局资源均用 xlarge 限定符标记,但设备是标准尺寸的屏幕)。
注:限定符的优先顺序(表 2 中)比与设备完全匹配的限定符数量更加重要。例如,在上面的第 4 步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(方向、触摸屏类型和输入法),而 drawable-en 只有一个匹配参数(语言)。但是,语言的优先顺序高于其他两个限定符,因此 drawable-port-notouch-12key 被淘汰。