应用资源概览

资源是您的代码使用的额外文件和静态内容,例如位图、布局定义、用户界面字符串、动画指令等。

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

外部化应用资源后,您可以使用项目 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)或编译为以下 Drawable 资源子类型的 XML 文件

  • 位图文件
  • Nine-patch(可调整大小的位图)
  • 状态列表
  • 形状
  • 动画 Drawable
  • 其他 Drawable

如需了解详情,请参阅Drawable 资源

mipmap/ 适用于不同启动器图标密度的 Drawable 文件。如需了解如何使用 mipmap/ 文件夹管理启动器图标,请参阅将应用图标放入 mipmap 目录
layout/ 定义用户界面布局的 XML 文件。如需了解详情,请参阅布局资源
menu/ 定义应用菜单的 XML 文件,例如选项菜单、上下文菜单或子菜单。如需了解详情,请参阅菜单资源
raw/

按原始形式保存任意文件。如需使用原始 InputStream 打开这些资源,请调用 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/ 任意 XML 文件,可通过调用 Resources.getXML() 在运行时读取。各种 XML 配置文件必须保存在此处,例如搜索配置
font/ 扩展名为 TTF、OTF 或 TTC 的字体文件,或包含 <font-family> 元素的 XML 文件。如需了解如何将字体用作资源,请参阅将字体添加为 XML 资源

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

如需了解有关各个资源类型的更多信息,请参阅资源类型概览

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

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

提供替代资源

大多数应用提供替代资源来支持特定的设备配置。例如,为不同的屏幕密度包含替代 drawable 资源,为不同的语言包含替代字符串资源。在运行时,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 修饰符表示该目录中的资源适用于高密度屏幕设备。这些 drawable 目录中的图像针对特定的屏幕密度进行了尺寸调整,但文件名完全相同。这样,您用于引用 icon.pngbackground.png 图像的资源 ID 始终是相同的。Android 通过将设备配置信息与资源目录名称中的修饰符进行比较,选择最符合当前设备的资源版本。

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

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

表 2. 配置修饰符名称。

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

移动国家/地区代码 (MCC),可选地后跟设备 SIM 卡中的移动网络代码 (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 表示“从左到右的布局方向”,是默认的隐式值。

这可以应用于任何资源,例如布局、Drawable 或值。

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

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> dp 的可用宽度。

例如,如果您的布局要求屏幕区域的最小尺寸始终至少为 600 dp,则可以使用此修饰符在 res/layout-sw600dp/ 目录中创建布局资源。系统仅在可用屏幕的最小维度至少为 600 dp 时使用这些资源,无论 600 dp 一侧是用户感知到的高度还是宽度。如果窗口被调整大小(改变可用宽度/高度)或重新定位(可能改变系统插页),最小宽度可能会发生变化。

使用最小宽度来确定大致的屏幕尺寸很有用,因为宽度通常是设计布局的关键因素。界面通常垂直滚动,但在水平方向所需的最小空间方面有相当严格的限制。

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

设备的最小宽度考虑了屏幕装饰和系统界面。例如,如果设备屏幕上有持久存在的界面元素,它们占据了最小宽度轴上的空间,则系统会将最小宽度声明为小于实际屏幕尺寸,因为这些屏幕像素不可用于您的界面。

您可以在此处用于常见屏幕尺寸的一些值

  • 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
等等。

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

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

当您的应用提供多个资源目录,且这些目录针对这些配置具有不同值时,系统会使用最接近(但不超过)设备当前屏幕宽度的那个。最接近的判定方法是:将实际屏幕宽度与指定宽度之间的差值加上实际屏幕高度与指定高度之间的差值,其中未指定的高度和宽度取值为 0。

这些值不包括被 Window 插页占用的区域,因此如果设备的显示屏边缘有持久的界面元素,则宽度和高度的值会小于实际屏幕尺寸,即使应用使用 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 配置字段,该字段指示屏幕是 small、normal 还是 large。

如需了解详情,请参阅屏幕兼容性概览

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

在 API 级别 4 中添加。

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

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

圆形屏幕 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 配置字段,该字段指示当前的设备方向。

界面模式 car
desk
television
appliance
watch
vrheadset
  • car:设备在车载底座中显示
  • desk:设备在桌面底座中显示
  • television:设备显示在电视上,提供“十英尺”体验,其界面显示在用户远离的大屏幕上,并且体验主要围绕方向键或其他非指针交互
  • appliance:设备作为器具使用,无显示屏
  • watch:设备有显示屏并佩戴在手腕上
  • vrheadset:设备在虚拟现实头戴设备中显示

在 API 级别 8 中添加;television 在 API 13 中添加;watch 在 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:匹配所有屏幕密度并优先于其他修饰符。这对于矢量 Drawable 很有用。在 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 级别。例如,v1 表示 API 级别 1(搭载 Android 1.0 或更高版本的设备),v4 表示 API 级别 4(搭载 Android 1.6 或更高版本的设备)。有关这些值的更多信息,请参阅Android API 级别文档。

注意:并非所有版本的 Android 都支持所有修饰符。使用新的修饰符会隐式添加平台版本修饰符,以便旧设备可以忽略它。例如,使用 w600dp 修饰符会自动包含 v13 修饰符,因为可用宽度修饰符是在 API 级别 13 中新增的。为避免任何问题,请务必包含一套默认资源(一组不带修饰符的资源)。如需了解详情,请参阅关于使用资源提供最佳设备兼容性的部分。

修饰符命名规则

以下是有关使用配置修饰符名称的一些规则

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

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

如果没有与特定设备配置匹配的替代资源,则 Android 会使用相应的默认资源——即不包含配置修饰符的特定资源类型的资源集。

创建别名资源

当您有一个希望用于多种设备配置但又不希望将其作为默认资源的资源时,无需将同一资源放在多个替代资源目录中。相反,您可以创建替代资源,该资源充当保存在默认资源目录中的资源的别名。

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

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

Drawable

要创建现有 drawable 的别名,请使用 <drawable> 元素

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

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

布局

要创建现有布局的别名,请使用嵌套在 <merge> 中的 <include> 元素

<?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 子类,例如所有 drawable 资源的 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 作为方法参数传递来在代码中使用资源。例如,您可以使用 setImageResource()ImageView 设置为使用 res/drawable/myimage.png 资源

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 中的方法检索单个资源,您可以通过 Context.getResources() 获取其实例。

语法

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

[<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 中使用资源来表示某个值,例如将 drawable 图像应用于微件,但您也可以在 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 中使用资源来创建别名。例如,您可以创建一个 drawable 资源,该资源是另一个 drawable 资源的别名

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

这听起来可能有点多余,但在使用替代资源时非常有用。如需了解详情,请参阅关于创建别名资源的部分。

引用样式属性

样式属性资源允许您引用当前应用主题中属性的值。引用样式属性可以让您通过将其样式设置为匹配当前主题提供的标准变体来定制 UI 元素的外观,而不是提供硬编码值。引用样式属性本质上是说:“使用当前主题中由该属性定义的样式。”

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

?[<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. 排除与设备配置冲突的资源文件。

    排除 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. 是否有任何资源目录包含此限定符?
    • 如果否,则返回步骤二,查看下一个限定符。在此示例中,在达到语言限定符之前,答案始终是“否”。
    • 如果是,则继续执行步骤四。
  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