应用资源概述

资源是代码使用的附加文件和静态内容,例如位图、布局定义、用户界面字符串、动画说明等等。

始终将应用资源(例如图像和字符串)从代码中外部化,以便您可以独立维护它们。另外,通过将资源分组到特殊命名的资源目录中,为特定设备配置提供替代资源。在运行时,Android 将根据当前配置使用相应的资源。例如,您可能希望根据屏幕尺寸提供不同的 UI 布局,或根据语言设置提供不同的字符串。

将应用资源外部化后,可以使用在项目 R 类中生成的资源 ID 访问它们。本文档介绍如何在 Android 项目中分组资源。它还介绍如何为特定设备配置提供替代资源,然后从应用代码或其他 XML 文件中访问它们。

分组资源类型

将每种类型的资源放在项目 res/ 目录的特定子目录中。例如,以下是一个简单项目的目录结构

MyProject/
    src/
        MyActivity.java
    res/
        drawable/
            graphic.png
        layout/
            main.xml
            info.xml
        mipmap/
            icon.png
        values/
            strings.xml

res/ 目录包含其子目录中的所有资源:一个图像资源、两个布局资源、一个用于启动图标的 mipmap/ 目录和一个字符串资源文件。资源目录名称很重要,并在表 1 中进行了说明。

注意: 有关使用 mipmap 文件夹的更多信息,请参阅 将应用图标放在 mipmap 目录中.

表 1. 项目 res/ 目录中支持的资源目录。

目录 资源类型
animator/ 定义 属性动画 的 XML 文件。
anim/ 定义 补间动画 的 XML 文件。属性动画也可以保存在此目录中,但建议将 animator/ 目录用于属性动画,以区分这两种类型。
color/ 定义颜色状态列表的 XML 文件。有关更多信息,请参阅 颜色状态列表资源.
drawable/

编译成以下可绘制资源子类型的位图文件 (PNG、.9.png、JPG 或 GIF) 或 XML 文件

  • 位图文件
  • 九宫格 (可调整大小的位图)
  • 状态列表
  • 形状
  • 动画可绘制对象
  • 其他可绘制对象

有关更多信息,请参阅 可绘制资源.

mipmap/ 用于不同启动图标密度的可绘制文件。有关使用 mipmap/ 文件夹管理启动图标的更多信息,请参阅 将应用图标放在 mipmap 目录中.
layout/ 定义用户界面布局的 XML 文件。有关更多信息,请参见 布局资源
menu/ 定义应用程序菜单的 XML 文件,例如选项菜单、上下文菜单或子菜单。有关更多信息,请参见 菜单资源
raw/

以原始形式保存的任意文件。要使用原始 InputStream 打开这些资源,请使用资源 ID 调用 Resources.openRawResource(),该 ID 为 R.raw.filename

但是,如果您需要访问原始文件名和文件层次结构,请考虑将资源保存到 assets/ 目录中,而不是 res/raw/assets/ 中的文件不会被赋予资源 ID,因此您只能使用 AssetManager 读取它们。

values/

包含简单值的 XML 文件,例如字符串、整数和颜色。

其他 res/ 子目录中的 XML 资源文件根据 XML 文件名定义单个资源,而 values/ 目录中的文件则描述多个资源。对于此目录中的文件,<resources> 元素的每个子元素都定义一个单独的资源。例如,<string> 元素创建一个 R.string 资源,而 <color> 元素创建一个 R.color 资源。

由于每个资源都使用自己的 XML 元素定义,因此您可以随意命名文件,并将不同的资源类型放置在一个文件中。但是,为了清晰起见,您可能希望将唯一的资源类型放置在不同的文件中。例如,以下是您可以在此目录中创建的资源的一些文件名约定

有关更多信息,请参见 字符串资源样式资源更多资源类型

xml/ 可以通过调用 Resources.getXML() 在运行时读取的任意 XML 文件。各种 XML 配置文件必须保存到这里,例如 搜索配置
font/ 扩展名为 TTF、OTF 或 TTC 的字体文件,或包含 <font-family> 元素的 XML 文件。有关字体作为资源的更多信息,请参见 将字体添加为 XML 资源

注意: 切勿将资源文件直接保存到 res/ 目录中。这会导致编译器错误。

有关各个资源类型的更多信息,请参见 资源类型概述

您在表 1 中定义的子目录中保存的资源是您的默认资源。也就是说,这些资源定义了应用程序的默认设计和内容。但是,不同类型的 Android 设备可能需要不同类型的资源。

例如,您可以为具有比正常屏幕更大的屏幕的设备提供不同的布局资源,以利用额外的屏幕空间。您还可以提供不同的字符串资源,根据设备的语言设置翻译用户界面中的文本。要为不同的设备配置提供这些不同的资源,您需要除了默认资源之外还提供备用资源。

提供备用资源

大多数应用程序提供备用资源来支持特定设备配置。例如,包括针对不同屏幕密度的备用可绘制资源,以及针对不同语言的备用字符串资源。在运行时,Android 会检测当前设备配置并加载适合应用程序的资源。

图 1. 两台设备使用基于屏幕大小的不同布局资源。

要为一组资源指定特定于配置的备用资源,请执行以下操作

  1. res/ 中创建一个新目录,命名格式为 <resources_name>-<qualifier>
    • <resources_name> 是相应默认资源的目录名称(在表 1 中定义)。
    • <qualifier> 是一个名称,指定要使用这些资源的单个配置(在表 2 中定义)。

    您可以附加多个 <qualifier>。使用连字符分隔每个限定符。

    注意: 附加多个限定符时,您必须按照表 2 中列出的顺序放置它们。如果限定符的顺序不正确,则资源将被忽略。

  2. 将适当的备用资源保存在此新目录中。资源文件必须与默认资源文件完全同名。

例如,以下是一些默认资源和备用资源

res/
    drawable/
        icon.png
        background.png
    drawable-hdpi/
        icon.png
        background.png

hdpi 限定符表示该目录中的资源适用于具有高密度屏幕的设备。这些可绘制目录中的图像针对特定屏幕密度进行了调整大小,但文件名完全相同。这样,用于引用 icon.pngbackground.png 图像的资源 ID 始终相同。Android 通过将设备配置信息与资源目录名称中的限定符进行比较,选择最适合当前设备的每个资源版本。

注意: 定义备用资源时,请确保您还在默认配置中定义该资源。否则,当设备更改配置时,您的应用程序可能会遇到运行时异常。例如,如果您只在 values-en 中添加一个字符串,而不在 values 中添加,当用户更改默认系统语言时,您的应用程序可能会遇到 Resource Not Found 异常。

表 2 按优先级顺序列出了有效的配置限定符。您可以通过使用连字符分隔每个限定符,将多个限定符添加到一个目录名称中。如果您为资源目录使用多个限定符,则必须按照表中列出的顺序将它们添加到目录名称中。

表 2. 配置限定符名称。

配置 限定符值 描述
MCC 和 MNC 示例
mcc310
mcc310-mnc004
mcc208-mnc00

来自设备中 SIM 卡的移动国家代码 (MCC),可以选择后跟移动网络代码 (MNC)。例如,mcc310 是美国任何运营商,mcc310-mnc004 是美国 Verizon,而 mcc208-mnc00 是法国 Orange。

如果设备使用无线电连接(即它是一部 GSM 手机),则 MCC 和 MNC 值来自 SIM 卡。

您也可以单独使用 MCC,例如,在您的应用程序中包含特定于国家的法律资源。如果您只需要根据语言进行指定,那么请使用 *语言、脚本(可选)和区域(可选)* 限定符。如果您使用 MCC 和 MNC 限定符,请谨慎使用并测试其是否按预期工作。

另请参见配置字段 mccmnc,它们分别指示当前的移动国家代码和移动网络代码。

语言、脚本(可选)和区域(可选) 示例
en
fr
en-rUS
fr-rFR
fr-rCA
b+en
b+en+US
b+es+419
b+zh+Hant
b+sr+Latn+RS

语言由两个字母的 ISO 639-1 语言代码定义,可以选择后跟两个字母的 ISO 3166-1-alpha-2 区域代码(以小写字母 r 作为前缀)。

这些代码 *不* 区分大小写。r 前缀用于区分区域部分。您不能单独指定区域。

Android 7.0(API 级别 24)引入了对 BCP 47 语言标签 的支持,您可以使用它们来限定特定于语言和区域的资源。语言标签由一个或多个子标签的序列组成,每个子标签都细化或缩小由整个标签标识的语言范围。有关语言标签的更多信息,请参见 语言识别标签

要使用 BCP 47 语言标签,请连接 b+ 和两个字母的 ISO 639-1 语言代码,可以选择后跟由 + 分隔的附加子标签。

如果用户在系统设置中更改其语言,语言标签可能会在应用程序的运行期间发生变化。有关这如何在运行时影响应用程序的信息,请参见 处理配置更改

有关将应用程序本地化为其他语言的完整指南,请参见 本地化应用程序

另请参见 getLocales() 方法,它提供了定义的语言环境列表。此列表包含主语言环境。

布局方向 ldrtl
ldltr

应用程序的布局方向。ldrtl 表示“布局方向从右到左”。ldltr 表示“布局方向从左到右”,是默认的隐式值。

这可以应用于任何资源,例如布局、可绘制对象或值。

例如,如果您想为阿拉伯语提供一个特定布局,而为任何其他“从右到左”的语言(如波斯语或希伯来语)提供一个通用布局,那么您可以使用以下目录

res/
  layout/
    main.xml(默认布局)
  layout-ar/
    main.xml(阿拉伯语的特定布局)
  layout-ldrtl/
    main.xml(任何从右到左的语言,但阿拉伯语除外,因为“ar”语言限定符具有更高的优先级)

注意: 要为应用程序启用从右到左的布局功能,您必须将 SupportsRtl 设置为 "true",并将 TargetSdkVersion 设置为 17 或更高。

在 API 级别 17 中添加。

最小宽度 sw<N>dp

示例
sw320dp
sw600dp
sw720dp
等等。

应用程序可用的屏幕区域的短边。具体来说,应用程序窗口的 smallestWidth 是窗口的可用高度和宽度中较短的一边。您也可以将其视为窗口的“最小可能宽度”。您可以使用此限定符,以便您的应用程序至少拥有 <N> dps 的宽度用于其 UI。

例如,如果您的布局要求屏幕区域的最小尺寸始终至少为 600 dp,那么您可以使用此限定符在 res/layout-sw600dp/ 目录中创建布局资源。系统仅在可用屏幕的最小尺寸至少为 600 dp 时使用这些资源,而无论 600 dp 侧是用户感知的高度还是宽度。如果窗口大小调整,更改可用宽度/高度,或者重新定位,可能会更改系统内边距,最小宽度可能会改变。

使用最小宽度来确定一般屏幕尺寸很有用,因为宽度通常是设计布局的驱动因素。UI 通常垂直滚动,但对它在水平方向上需要的最小空间有相当严格的约束。

可用宽度也是决定是否使用手机的单窗格布局或平板电脑的多窗格布局的关键因素。因此,您最关心的可能是每台设备上可能的最小宽度是多少。

设备的最小宽度考虑了屏幕装饰和系统 UI。例如,如果设备在屏幕上具有沿最小宽度轴占用空间的持久 UI 元素,则系统会声明最小宽度小于实际屏幕大小,因为这些是您的 UI 不可用的屏幕像素。

您可能会在这里使用的一些常见屏幕尺寸的值

  • 320,用于具有以下屏幕配置的设备
    • 240x320 ldpi (QVGA 手机)
    • 320x480 mdpi (手机)
    • 480x800 hdpi (高密度手机)
  • 480,用于 480x800 mdpi (平板电脑/手机) 等屏幕
  • 600,用于 600x1024 mdpi (7 英寸平板电脑) 等屏幕
  • 720,用于 720x1280 mdpi (10 英寸平板电脑) 等屏幕

当您的应用提供多个具有不同 smallestWidth 限定符值的资源目录时,系统会使用最接近(不超过)设备 smallestWidth 的一个。

在 API 级别 13 中添加。

另请参阅 android:requiresSmallestWidthDp 属性,该属性声明您的应用兼容的最小 smallestWidth,以及 smallestScreenWidthDp 配置字段,该字段保存设备的 smallestWidth 值。

有关使用此限定符为不同屏幕设计更多信息,请参阅 使用视图的响应式/自适应设计

可用宽度和高度 w<N>dp
h<N>dp

示例
w720dp
w1024dp
h720dp
h1024dp
等等。

指定使用资源的最小可用屏幕宽度或高度(以 dp 单位表示,由 <N> 值定义)。这些配置值与当前显示宽度和高度进行比较,因为设备方向在纵向和横向之间切换、设备折叠或展开,或者系统进入或退出多窗口模式。在多窗口模式下,这些值反映的是包含应用的窗口的宽度和高度,而不是设备屏幕的宽度和高度。同样,对于嵌入式活动,这些值与单个活动的宽度和高度有关,而不是屏幕的宽度和高度。有关更多信息,请参阅 活动嵌入

可用宽度和高度通常用于确定是否使用多窗格布局,因为即使在平板电脑设备上,您也通常不希望纵向和横向具有相同的布局。因此,您可以使用这些值来指定布局所需的最小宽度和/或高度,而不是同时使用屏幕尺寸和方向限定符。

当您的应用提供多个具有不同配置值的资源目录时,系统会使用最接近(不超过)设备当前屏幕宽度的那个。最接近 是通过将实际屏幕宽度与指定宽度之间的差值加到实际屏幕高度与指定高度之间的差值来确定的,其中未指定的宽度和高度的值为 0。

这些值不包括 窗口内边距 占用的区域,因此如果设备在显示器的边缘具有持久 UI 元素,即使应用使用 Window.setDecorFitsSystemWindows WindowCompat.setDecorFitsSystemWindows 处于边缘对边缘显示状态,宽度和高度的值也会小于实际屏幕尺寸。

某些不是固定的垂直屏幕装饰(例如全屏时可以隐藏的手机状态栏)不会 被计入这里,窗口装饰(如标题栏或操作栏)也不会被计入,因此应用必须准备好处理比它们指定的小的区域。

注意: 系统选择匹配宽度和高度的资源。因此,与仅指定一个或另一个的资源相比,指定两个的资源更受青睐。例如,如果实际屏幕是 720 dp 宽、1280 dp 高,并且一个资源限定为 w720dp,另一个资源限定为 w700dp-h1200dp,那么即使前者是其指定内容的精确匹配,也会选择后者。

在 API 级别 13 中添加。

另请参阅 screenWidthDpscreenHeightDp 配置字段,这些字段保存当前屏幕宽度和高度。

有关使用此限定符为不同屏幕设计更多信息,请参阅 使用视图的响应式/自适应设计

屏幕尺寸 small
normal
large
xlarge
  • small:与低密度 QVGA 屏幕尺寸相似的屏幕。小型屏幕的最小布局尺寸约为 320x426 dp 单位。例如,QVGA 低密度和 VGA 高密度。
  • normal:与中等密度 HVGA 屏幕尺寸相似的屏幕。普通屏幕的最小布局尺寸约为 320x470 dp 单位。此类屏幕的示例包括 WQVGA 低密度、HVGA 中等密度和 WVGA 高密度。
  • large:与中等密度 VGA 屏幕尺寸相似的屏幕。大型屏幕的最小布局尺寸约为 480x640 dp 单位。例如,VGA 和 WVGA 中等密度屏幕。
  • xlarge:比传统的中等密度 HVGA 屏幕大得多的屏幕。超大型屏幕的最小布局尺寸约为 720x960 dp 单位。在大多数情况下,具有超大型屏幕的设备太大而无法放在口袋里,并且很可能都是平板电脑风格的设备。在 API 级别 9 中添加。

注意: 使用尺寸限定符并不意味着这些资源 用于该尺寸的屏幕。如果您没有提供使用更适合当前设备配置的限定符的替代资源,系统可以使用任何 最佳匹配 的资源。

警告: 如果所有资源都使用大于 当前屏幕的尺寸限定符,则系统不会 使用它们,并且您的应用会在运行时崩溃。例如,如果所有布局资源都用 xlarge 限定符标记,但设备具有普通大小的屏幕,就会发生这种情况。

在 API 级别 4 中添加。

另请参阅 screenLayout 配置字段,该字段指示屏幕是小型、普通还是大型。

有关更多信息,请参阅 屏幕兼容性概述

屏幕纵横比 long
notlong
  • long:长屏幕,例如 WQVGA、WVGA、FWVGA
  • notlong:非长屏幕,例如 QVGA、HVGA 和 VGA

在 API 级别 4 中添加。

这纯粹是根据屏幕的纵横比(long 屏幕更宽)。这与屏幕方向无关。

另请参阅 screenLayout 配置字段,该字段指示屏幕是否为长屏幕。

圆形屏幕 round
notround
  • round:圆形屏幕,例如圆形可穿戴设备
  • notround:矩形屏幕,例如手机或平板电脑

在 API 级别 23 中添加。

另请参阅 isScreenRound() 配置方法,该方法指示屏幕是否为圆形。

广色域 widecg
nowidecg
  • widecg:具有广色域的显示器,例如 Display P3 或 AdobeRGB
  • nowidecg:具有窄色域的显示器,例如 sRGB

在 API 级别 26 中添加。

另请参阅 isScreenWideColorGamut() 配置方法,该方法指示屏幕是否具有广色域。

高动态范围 (HDR) highdr
lowdr
  • highdr:具有高动态范围的显示器
  • lowdr:具有低/标准动态范围的显示器

在 API 级别 26 中添加。

另请参阅 isScreenHdr() 配置方法,该方法指示屏幕是否具有 HDR 功能。

屏幕方向 port
land
  • port:设备处于纵向(垂直)方向
  • land:设备处于横向(水平)方向

如果用户旋转屏幕,这会在您的应用的生命周期中发生变化。有关这如何在运行时影响您的应用的信息,请参阅 处理配置更改

另请参阅 orientation 配置字段,该字段指示当前设备方向。

UI 模式 car
desk
television
appliance
watch
vrheadset
  • car:设备在汽车扩展坞中显示
  • desk:设备在桌面扩展坞中显示
  • television:设备在电视上显示,提供“十英尺”体验,其中其 UI 在用户远离的大屏幕上,体验主要围绕方向键或其他非指针交互进行
  • appliance:设备充当电器,没有显示器
  • watch:设备具有显示器,戴在手腕上
  • vrheadset:设备在虚拟现实头显中显示

在 API 级别 8 中添加;在 API 13 中添加了电视;在 API 20 中添加了手表。

有关您的应用如何在设备插入或从扩展坞中取出时做出响应的信息,请阅读 确定和监控扩展坞状态和类型

如果用户将设备放置在扩展坞中,这会在您的应用的生命周期中发生变化。您可以使用 UiModeManager 启用或禁用这些模式中的某些模式。有关这如何在运行时影响您的应用的信息,请参阅 处理配置更改

夜间模式 night
notnight
  • night:晚上
  • notnight:白天

在 API 级别 8 中添加。

如果将夜间模式留在自动模式(默认),则此模式在应用程序的生命周期内可能会发生变化,在这种情况下,模式会根据一天中的时间变化。您可以使用 UiModeManager 启用或禁用此模式。有关此模式在运行时如何影响应用程序的信息,请参阅 处理配置更改

屏幕像素密度 (dpi) ldpi
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
nnndpi
  • ldpi:低密度屏幕;大约 120 dpi。
  • mdpi:中等密度(在传统 HVGA)屏幕;大约 160 dpi。
  • hdpi:高密度屏幕;大约 240 dpi。
  • xhdpi:超高密度屏幕;大约 320 dpi。在 API 级别 8 中添加。
  • xxhdpi:超超高密度屏幕;大约 480 dpi。在 API 级别 16 中添加。
  • xxxhdpi:超超超高密度使用(仅限启动器图标 - 请参阅 支持不同的像素密度);大约 640 dpi。在 API 级别 18 中添加。
  • nodpi:用于您不希望缩放以匹配设备密度的位图资源。
  • tvdpi:介于 mdpi 和 hdpi 之间的屏幕;大约 213 dpi。这不被视为“主要”密度组。它主要用于 720p 电视,大多数应用程序不需要它。对于 1080p 电视面板,请使用 xhdpi,对于 4K 电视面板,请使用 xxxhdpi在 API 级别 13 中添加。
  • anydpi:匹配所有屏幕密度,并优先于其他限定符。这对于 矢量可绘制对象 很有用。在 API 级别 21 中添加。
  • nnndpi:用于表示非标准密度,其中 nnn 是一个正整数屏幕密度。这在大多数情况下不使用。使用标准密度桶可以大大减少支持市场上各种设备屏幕密度的开销。

六种主要密度(忽略 tvdpi 密度)之间存在 3:4:6:8:12:16 的缩放比例。因此,ldpi 中的 9x9 位图在 mdpi 中为 12x12,在 hdpi 中为 18x18,在 xhdpi 中为 24x24,等等。

注意:使用密度限定符并不意味着资源适用于该密度的屏幕。如果您没有提供具有更匹配当前设备配置的限定符的替代资源,系统将使用最匹配的资源。

有关如何处理不同的屏幕密度以及 Android 如何缩放位图以适合当前密度的更多信息,请参阅 屏幕兼容性概述

触摸屏类型 notouch
finger
  • notouch:设备没有触摸屏。
  • finger:设备具有触摸屏,旨在通过用户手指的直接交互来使用。

另请参阅 touchscreen 配置字段,该字段指示设备上触摸屏的类型。

键盘可用性 keysexposed
keyshidden
keyssoft
  • keysexposed:设备有键盘可用。如果设备启用了软件键盘(很可能),则即使硬件键盘暴露给用户或设备没有硬件键盘时,也会使用此功能。如果没有提供软件键盘或软件键盘已禁用,则仅当硬件键盘暴露时才会使用此功能。
  • keyshidden:设备有硬件键盘可用,但它被隐藏并且设备没有启用软件键盘。
  • keyssoft:设备启用了软件键盘,无论它是否可见。

如果您提供 keysexposed 资源,但没有提供 keyssoft 资源,则系统将使用 keysexposed 资源,无论键盘是否可见,只要系统启用了软件键盘即可。

如果用户打开硬件键盘,这可能会在应用程序的生命周期内发生变化。有关此模式在运行时如何影响应用程序的信息,请参阅 处理配置更改

另请参阅配置字段 hardKeyboardHiddenkeyboardHidden,它们分别指示硬件键盘的可见性和任何类型键盘(包括软件)的可见性。

主要文本输入方法 nokeys
qwerty
12key
  • nokeys:设备没有用于文本输入的硬件键。
  • qwerty:设备具有硬件 QWERTY 键盘,无论它是否对用户可见。
  • 12key:设备具有硬件 12 键键盘,无论它是否对用户可见。

另请参阅 keyboard 配置字段,该字段指示可用的主要文本输入方法。

平台版本(API 级别) 示例
v3
v4
v7
等等。

设备支持的 API 级别。例如,API 级别 1(具有 Android 1.0 或更高版本的设备)为 v1,API 级别 4(具有 Android 1.6 或更高版本的设备)为 v4。有关这些值的更多信息,请参阅 Android API 级别 文档。

注意:并非所有版本的 Android 都支持所有限定符。使用新的限定符会隐式添加平台版本限定符,以便旧设备可以忽略它。例如,使用 w600dp 限定符会自动包含 v13 限定符,因为可用宽度限定符是在 API 级别 13 中新添加的。为了避免任何问题,始终包含一组默认资源(一组没有限定符的资源)。有关更多信息,请参阅有关 使用资源提供最佳设备兼容性 的部分。

限定符名称规则

以下是一些关于使用配置限定符名称的规则

  • 您可以为一组资源指定多个限定符,用连字符分隔。例如,drawable-en-rUS-land 适用于横向模式下的美国英语设备。
  • 限定符必须按照 表格 2 中列出的顺序排列。
    • 错误:drawable-hdpi-port/
    • 正确:drawable-port-hdpi/
  • 替代资源目录不能嵌套。例如,您不能拥有 res/drawable/drawable-en/
  • 值不区分大小写。资源编译器在处理之前将目录名称转换为小写,以避免在不区分大小写的文件系统上出现问题。名称中的任何大写仅是为了便于阅读。
  • 每个限定符类型仅支持一个值。例如,如果您想对西班牙和法国使用相同的可绘制文件,您不能拥有名为 drawable-es-fr/ 的目录。相反,您需要两个资源目录,例如 drawable-es/drawable-fr/,其中包含相应的文件。但是,您并不需要实际将文件复制到两个位置。相反,您可以为资源创建别名,如 创建别名资源 部分所述。

将替代资源保存到使用这些限定符命名的目录中后,Android 会根据当前设备配置自动将资源应用到您的应用程序中。每次请求资源时,Android 都会检查包含请求的资源文件的替代资源目录,然后 查找最匹配的资源

如果没有与特定设备配置匹配的替代资源,则 Android 将使用相应的默认资源 - 针对特定资源类型的不包含配置限定符的资源集。

创建别名资源

当您有一个想要在多个设备配置中使用的资源,但不想将其作为默认资源提供时,您不需要将相同的资源放在多个替代资源目录中。相反,您可以创建一个替代资源,该资源充当保存到默认资源目录中的资源的别名。

注意:并非所有资源都提供创建另一个资源别名的机制。特别是,xml/ 目录中的动画、菜单、原始和其他未指定的资源不提供此功能。

例如,假设您有一个应用程序图标 icon.png,并且需要为不同的语言环境提供其唯一版本。但是,两种语言环境,加拿大英语和加拿大法语,需要使用同一个版本。您不需要将同一个图像复制到加拿大英语和加拿大法语的资源目录中。相反,您可以使用 icon.png 以外的任何名称保存用于这两个语言环境的图像,例如 icon_ca.png,并将其放入默认的 res/drawable/ 目录中。然后创建一个 icon.xml 文件,放在 res/drawable-en-rCA/res/drawable-fr-rCA/ 中,使用 <bitmap> 元素引用 icon_ca.png 资源。这样,您就可以只存储一个版本的 PNG 文件和两个指向它的小型 XML 文件。有关详细信息,请参阅以下部分中的示例。

可绘制对象

要创建现有可绘制对象的别名,请使用 <drawable> 元素

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <drawable name="icon">@drawable/icon_ca</drawable>
</resources>

如果您将此文件保存为 icon.xml,放在替代资源目录(例如 res/values-en-rCA/)中,它将编译成一个资源,您可以将其引用为 R.drawable.icon,但实际上是 R.drawable.icon_ca 资源的别名,该资源保存在 res/drawable/ 中。

布局

要创建现有布局的别名,请使用 <include> 元素,将其包装在 <merge>

<?xml version="1.0" encoding="utf-8"?>
<merge>
    <include layout="@layout/main_ltr"/>
</merge>

如果您将此文件保存为 main.xml,它将编译成一个资源,您可以将其引用为 R.layout.main,但实际上是 R.layout.main_ltr 资源的别名。

字符串和其他简单值

要创建对现有字符串的别名,请使用所需字符串的资源 ID 作为新字符串的值。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello</string>
    <string name="hi">@string/hello</string>
</resources>

R.string.hi 资源现在是 R.string.hello 的别名。

其他简单值 的工作方式相同,例如颜色。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="red">#f00</color>
    <color name="highlight">@color/red</color>
</resources>

访问您的应用程序资源

在应用程序中提供资源后,可以通过引用其资源 ID 来应用它。所有资源 ID 都在项目的 R 类中定义,该类由 aapt 工具自动生成。

编译应用程序时,aapt 会生成 R 类,该类包含 res/ 目录中所有资源的资源 ID。对于每种类型的资源,都存在一个 R 子类,例如,R.drawable 用于所有可绘制资源。对于该类型的每个资源,都有一个静态整数,例如,R.drawable.icon。此整数是用于检索资源的资源 ID。

虽然 R 类是指定资源 ID 的位置,但您无需查看该类来发现资源 ID。资源 ID 始终由以下部分组成:

  • 资源类型:每个资源都分组到一个“类型”中,例如 stringdrawablelayout。有关不同类型的更多信息,请参阅 资源类型概述
  • 资源名称,即不含扩展名的文件名或 XML android:name 属性中的值(如果资源是简单值,例如字符串)。

您可以通过两种方式访问资源:

  • 在代码中:使用 R 类的子类的静态整数,例如:
    R.string.hello

    string 是资源类型,hello 是资源名称。当您以这种格式提供资源 ID 时,许多 Android API 可以访问您的资源。有关更多信息,请参阅 在代码中访问资源 部分。

  • 在 XML 中:使用与 R 类中定义的资源 ID 相对应的特殊 XML 语法,例如:
    @string/hello

    string 是资源类型,hello 是资源名称。您可以在预期使用资源中提供的值的 XML 资源中的任何位置使用此语法。有关更多信息,请参阅 从 XML 访问资源 部分。

在代码中访问资源

您可以通过将资源 ID 作为方法参数传递来在代码中使用资源。例如,可以使用 ImageView 设置 res/drawable/myimage.png 资源,方法是使用 setImageResource()

Kotlin

val imageView = findViewById(R.id.myimageview) as ImageView
imageView.setImageResource(R.drawable.myimage)

Java

ImageView imageView = (ImageView) findViewById(R.id.myimageview);
imageView.setImageResource(R.drawable.myimage);

您还可以使用 Resources 中的方法检索单个资源,您可以使用 getResources() 获取 Resources 的实例。

语法

以下是引用代码中资源的语法:

[<package_name>.]R.<resource_type>.<resource_name>
  • <package_name> 是资源所在的包的名称(引用您自己包中的资源时不需要)。
  • <resource_type> 是资源类型的 R 子类。
  • <resource_name> 是不含扩展名的资源文件名或 XML 元素中的 android:name 属性值(对于简单值)。

有关每种资源类型及其引用的更多信息,请参阅 资源类型概述

用例

许多方法接受资源 ID 参数,您可以使用 Resources 中的方法检索资源。您可以使用 Context.getResources() 获取 Resources 的实例。

以下是访问代码中资源的一些示例:

Kotlin

// Load a background for the current screen from a drawable resource.
window.setBackgroundDrawableResource(R.drawable.my_background_image)

// Set the Activity title by getting a string from the Resources object, because
//  this method requires a CharSequence rather than a resource ID.
window.setTitle(resources.getText(R.string.main_title))

// Load a custom layout for the current screen.
setContentView(R.layout.main_screen)

// Set a slide in animation by getting an Animation from the Resources object.
flipper.setInAnimation(AnimationUtils.loadAnimation(this,
        R.anim.hyperspace_in))

// Set the text on a TextView object using a resource ID.
val msgTextView = findViewById(R.id.msg) as TextView
msgTextView.setText(R.string.hello_message)

Java

// Load a background for the current screen from a drawable resource.
getWindow().setBackgroundDrawableResource(R.drawable.my_background_image) ;

// Set the Activity title by getting a string from the Resources object, because
//  this method requires a CharSequence rather than a resource ID.
getWindow().setTitle(getResources().getText(R.string.main_title));

// Load a custom layout for the current screen.
setContentView(R.layout.main_screen);

// Set a slide in animation by getting an Animation from the Resources object.
flipper.setInAnimation(AnimationUtils.loadAnimation(this,
        R.anim.hyperspace_in));

// Set the text on a TextView object using a resource ID.
TextView msgTextView = (TextView) findViewById(R.id.msg);
msgTextView.setText(R.string.hello_message);

注意:不要手动修改 R.java 文件。它是通过编译项目时 aapt 工具生成的。下次编译时,任何更改都将被覆盖。

从 XML 访问资源

您可以使用对现有资源的引用来定义一些 XML 属性和元素的值。在创建布局文件时,通常这样做,以便为小部件提供字符串和图像。

例如,如果您将 Button 添加到布局中,请使用 字符串资源 作为按钮文本。

<Button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/submit" />

语法

以下是在 XML 资源中引用资源的语法:

@[<package_name>:]<resource_type>/<resource_name>
  • <package_name> 是资源所在的包的名称(引用同一包中的资源时不需要)。
  • <resource_type> 是资源类型的 R 子类。
  • <resource_name> 是不含扩展名的资源文件名或 XML 元素中的 android:name 属性值(对于简单值)。

有关每种资源类型及其引用的更多信息,请参阅 资源类型概述

用例

在某些情况下,您必须在 XML 中为值使用资源,例如将可绘制图像应用于小部件,但您也可以在 XML 中的任何接受简单值的位置使用资源。例如,如果您有以下包含 颜色资源字符串资源 的资源文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <color name="opaque_red">#f00</color>
   <string name="hello">Hello!</string>
</resources>

您可以在以下布局文件中使用这些资源来设置文本颜色和文本字符串:

<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:textColor="@color/opaque_red"
    android:text="@string/hello" />

在本例中,您不需要在资源引用中指定包名称,因为资源来自您自己的包。要引用系统资源,您需要包含包名称,如下面的示例所示:

<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:textColor="@android:color/secondary_text_dark"
    android:text="@string/hello" />

注意:始终使用字符串资源,以便您的应用程序可以本地化为其他语言。有关创建替代资源(例如本地化字符串)的信息,请参阅 提供替代资源。有关将应用程序本地化为其他语言的完整指南,请参阅 本地化应用程序

您甚至可以在 XML 中使用资源来创建别名。例如,您可以创建一个可绘制资源,它是另一个可绘制资源的别名:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/other_drawable" />

这听起来很冗余,但在使用替代资源时非常有用。有关更多信息,请参阅有关 创建别名资源 的部分。

引用样式属性

样式属性资源使您可以引用当前应用主题中属性的值。引用样式属性使您可以通过对 UI 元素进行样式化以匹配当前主题提供的标准变体来自定义 UI 元素的外观,而不是提供硬编码值。引用样式属性实际上表示“使用当前主题中此属性定义的样式”。

要引用样式属性,名称语法与普通资源格式几乎相同,但使用问号 (?) 而不是“at”符号 (@)。资源类型部分是可选的。因此,引用语法如下所示:

?[<package_name>:][<resource_type>/]<resource_name>

例如,以下是如何引用属性以将文本颜色设置为与系统主题的辅助文本颜色匹配:

<EditText id="text"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="?android:textColorSecondary"
    android:text="@string/hello_world" />

在此,android:textColor 属性指定了当前主题中样式属性的名称。现在,Android 使用应用于 android:textColorSecondary 样式属性的值作为此小部件中 android:textColor 的值。由于系统资源工具知道在此上下文中需要属性资源,因此您无需显式声明类型,即 ?android:attr/textColorSecondary。您可以排除 attr 类型。

访问原始文件

虽然不常见,但您可能需要访问原始文件和目录。如果确实如此,那么将文件保存在 res/ 中对您不起作用,因为从 res/ 读取资源的唯一方法是使用资源 ID。相反,您可以将资源保存在 assets/ 目录中。

保存在 assets/ 目录中的文件不会获得资源 ID,因此您无法通过 R 类或从 XML 资源中引用它们。相反,您可以像普通文件系统一样查询 assets/ 目录中的文件,并使用 AssetManager 读取原始数据。

但是,如果您只需要能够读取原始数据(例如视频或音频文件),那么将文件保存在 res/raw/ 目录中,并使用 openRawResource() 读取字节流。

访问平台资源

Android 包含许多标准资源,例如样式、主题和布局。要访问这些资源,请使用 android 包名称限定您的资源引用。例如,Android 提供了一种布局资源,您可以将其用于 ListAdapter 中的列表项:

Kotlin

listAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, myarray)

Java

setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myarray));

在此示例中,simple_list_item_1 是平台为 ListView 中的项定义的布局资源。您可以使用它,而不是创建自己的列表项布局。

使用资源提供最佳设备兼容性

为了使您的应用程序支持多种设备配置,始终为应用程序使用的每种类型的资源提供默认资源非常重要。

例如,如果您的应用程序支持多种语言,请始终包含一个 values/ 目录(您的字符串保存在其中)没有语言和区域限定符。如果您将所有字符串文件放在具有语言和区域限定符的目录中,那么应用程序在设置为应用程序字符串不支持的语言的设备上运行时会崩溃。

只要您提供默认的 values/ 资源,那么即使用户不理解应用程序呈现的语言,应用程序也能正常运行。这比崩溃要好。

同样,如果您根据屏幕方向提供不同的布局资源,请选择一个方向作为默认方向。例如,与其在 layout-land/ 中提供横向布局资源,在 layout-port/ 中提供纵向布局资源,不如将其中一个作为默认值,例如,将 layout/ 用于横向,将 layout-port/ 用于纵向。

提供默认资源非常重要,不仅因为您的应用可能在您没有预料到的配置上运行,而且因为 Android 的新版本有时会添加旧版本不支持的配置限定符。如果您使用新的资源限定符,但要保持与旧版 Android 的代码兼容性,那么当旧版 Android 运行您的应用时,如果没有提供默认资源,就会崩溃,因为它无法使用使用新限定符命名的资源。

例如,如果您的 minSdkVersion 设置为 4,并且您使用 夜间模式 (nightnotnight,它们是在 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 中选择可绘制资源。

系统使用以下逻辑确定使用哪些资源

图 2. Android 如何找到最佳匹配资源的流程图。

  1. 消除与设备配置相矛盾的资源文件。

    由于与 en-GB 区域设置相矛盾,因此 drawable-fr-rCA/ 目录被消除。

    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. 任何资源目录是否包含此限定符?
    • 如果否,请返回步骤二并查看下一个限定符。在此示例中,答案为“否”,直到到达语言限定符。
    • 如果是,请继续执行步骤四。
  4. 消除不包含此限定符的资源目录。在此示例中,系统接下来消除所有不包含语言限定符的目录
    drawable/
    drawable-en/
    drawable-en-port/
    drawable-en-notouch-12key/
    drawable-port-ldpi/
    drawable-port-notouch-12key/
    

    例外:如果所讨论的限定符是屏幕像素密度,Android 会选择最接近设备屏幕密度的选项。一般来说,Android 更倾向于将较大的原始图像缩小,而不是将较小的原始图像放大。有关更多信息,请参阅 屏幕兼容性概述

  5. 重复步骤二、三和四,直到只剩下一个目录。在此示例中,屏幕方向是下一个存在任何匹配项的限定符。因此,不指定屏幕方向的资源将被消除
    drawable-en/
    drawable-en-port/
    drawable-en-notouch-12key/
    

    剩余的目录是 drawable-en-port

尽管此过程针对请求的每个资源都执行,但系统会对其某些方面进行优化。一种优化是,一旦知道设备配置,它可能会消除永远不会匹配的备用资源。例如,如果配置语言是英语,那么任何语言限定符设置为非英语的资源目录都不会包含在要检查的资源池中(尽管没有语言限定符的资源目录仍然包含在内)。

根据屏幕大小限定符选择资源时,如果不存在与当前屏幕更好地匹配的资源,系统会使用为小于当前屏幕的屏幕设计的资源。例如,大型屏幕会根据需要使用普通大小屏幕的资源。

但是,如果唯一可用的资源大于当前屏幕,则系统不会使用它们,如果没有任何其他资源与设备配置匹配,您的应用将崩溃。例如,如果所有布局资源都标记为 xlarge 限定符,但设备是普通大小屏幕,就会发生这种情况。

注意:限定符的优先级(在 表格 2 中)比与设备完全匹配的限定符数量更重要。在上一个示例中,在步骤四中,列表中的最后一个选项包含三个与设备完全匹配的限定符(方向、触摸屏类型和输入方法),而 drawable-en 只有一个与之匹配的参数(语言)。但是,语言的优先级高于这些其他限定符,因此 drawable-port-notouch-12key 被消除。