概述
对于Android中的应用资源,例如图像、字符串、布局、颜色等,最好是在res文件夹中定义,而不是在代码中定义,这样有利于你单独维护这些资源。此外,你还应该为特定设备提供备用资源,将它们分组到专门命名的资源目录中,当运行时,Android会根据当前配置使用适当的资源。例如,你可能需要根据屏幕尺寸提供不同的UI布局,或者根据语言设置提供不同的字符串。
res/目录内支持的资源目录
名称 | 文件 | 描述 |
---|---|---|
属性动画 | animator | 定义property动画的XML文件 |
渐变动画 | anim | 定义tween动画的XML文件(属性动画也可以保存在此目录中,但是为了区分两种类型,属性动画首选animator 目录) |
颜色 | color | 用于定义颜色状态列表的XML文件。Color State List |
图片 | drawable | 位图文件(.png、.9.png、.jpg、.gif)或编译为可绘制对象资源(位图文件、九宫格、状态列表、形状、动画可绘制对象、其他可绘制对象)子类型的XML文件。Drawable State List |
布局 | layout | 定义UI布局的XML文件 |
mipmap | mipmap | 适用于不同像素密度的启动器图标的可绘制对象文件 |
菜单 | menu | 定义菜单或action bar的XML文件 |
raw | raw | 要以原始形式保存的任意文件,如果需要访问原始文件名和文件层次结构,则可以考虑将某些资源保存在assets 目录下(而不是res/raw) |
xml | xml | 可以在运行时通过调用Resources.getXML()读取的任意XML文件。各种XML配置文件(如可搜索配置)都必须保存在此处 |
值 | values | 定义strings、integers、colors等值的XML文件 |
颜色 | res/values/colors.xml | 定义例如文本颜色等颜色的XML文件 |
尺寸 | res/values/dimens.xml | 定义例如间距等尺寸的XML文件 |
字符串 | res/values/strings.xml | 定义例如标题文本等字符串的XML文件 |
样式 | res/values/styles.xml | 定义例如AppBar的颜色等样式的XML文件 |
注意:切勿将资源文件直接保存在res/
目录内,将会导致编译错误。
提供备用资源文件来适配各种设备配置
配置限定符名称
配置 | 限定符值 | 说明 |
---|---|---|
MCC和 MNC | 示例:mcc310、mcc310-mnc004、mcc208-mnc00等等 |
|
语言和区域 | 示例:en、fr、en-rUS、fr-rFR、fr-rCA等等 |
|
布局方向 | ldrtl、ldltr |
注意:要为应用启用从右到左的布局功能,必须将 supportsRtl设置为“true”,并将 targetSdkVersion设置为17或更高版本 |
smallestWidth | sw 例如:sw320dp、sw600dp等等 |
|
可用宽度 | w 例如:w720dp、w1024dp等等 |
指定资源使用的最小可用屏幕宽度,以dp为单位,由 |
可用高度 | h 例如:h720dp、h1024dp等等 |
与可用宽度类似,android:screenHeightDp存放当前屏幕高度 |
屏幕尺寸 | small、normal、large、xlarge |
screenLayout指示屏幕是小尺寸、标准尺寸还是大尺寸 |
屏幕纵横比 | long、notlong | long:宽屏,如 WQVGA、WVGA、FWVGA;notlong:非宽屏,如QVGA、HVGA和VGA,它完全基于屏幕的纵横比,而与屏幕方向无关。screenLayout指示屏幕是否为宽屏 |
圆形屏幕 | round、notround | round:圆形屏幕,例如圆形可穿戴式设备;notround:方形屏幕,例如手机或平板电脑。isScreenRound()方法指示屏幕是否为宽屏 |
屏幕方向 | port、land | port:设备处于纵向(垂直);land:设备处于横向(水平)。如果用户旋转屏幕,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。orientation指示当前的设备方向 |
UI 模式 | car、desk、television、appliance、watch | car:设备正在车载手机座上显示;desk:设备正在桌面手机座上显示;television:设备正在电视上显示,为用户提供“十英尺”体验,其 UI 位于远离用户的大屏幕上,主要面向方向键或其他非指针式交互;appliance:设备用作不带显示屏的装置;watch:设备配有显示屏,戴在手腕上。如需了解应用在设备插入手机座或从中移除时的响应方式,请阅读确定并监控插接状态和类型。如果用户将设备放入手机座中,它有可能在应用生命周期中发生改变。 可以使用 UiModeManager启用或禁用其中某些模式。如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 |
夜间模式 | night、notnight | night:夜间;notnight:白天。如果夜间模式停留在自动模式(默认),它有可能在应用生命周期中发生改变。在这种情况下,该模式会根据当天的时间进行调整。 可以使用 UiModeManager启用或禁用此模式。如需了解这会在运行期间给应用带来哪些响,请参阅处理运行时变更。 |
屏幕像素密度 (dpi) | ldpi、mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi、nodpi、tvdpi、anydpi | ldpi:低密度屏幕;约为 120dpi。mdpi:中等密度(传统 HVGA)屏幕;约为 160dpi。hdpi:高密度屏幕;约为 240dpi。xhdpi:超高密度屏幕;约为 320dpi。此项为 API 级别 8 中新增配置。xxhdpi:超超高密度屏幕;约为 480dpi。此项为 API 级别 16 中新增配置。xxxhdpi:超超超高密度屏幕使用(仅限启动器图标,请参阅“支持多种屏幕”中的注释);约为 640dpi。** 此项为 API 级别 18 中新增配置。nodpi:它可用于您不希望缩放以匹配设备密度的位图资源。tvdpi:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, 主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将根据需要对其进行缩放。此项为 API 级别 13 中新增配置 。anydpi:此限定符适合所有屏幕密度,其优先级高于其他限定符。 这对于矢量可绘制对象很有用。 此项为 API 级别 21 中新增配置。六个主要密度之间的缩放比为 3:4:6:8:12:16(忽略 tvdpi 密度)。因此,9x9 (ldpi) 位图相当于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图。如果您认为图像资源在电视或其他某些设备上呈现的效果不够好,而想尝试使用 tvdpi 资源,则缩放比例为 1.33*mdpi。例如,mdpi 屏幕的 100px x 100px 图像应该相当于 tvdpi 的133px x 133px。注:使用密度限定符并不表示资源仅适用于该密度的屏幕。 如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。 |
触摸屏类型 | notouch、finger | notouch:设备没有触摸屏;finger:设备有一个专供用户通过手指直接与其交互的触摸屏。touchscreen指示设备上的触摸屏类型。 |
键盘可用性 | keysexposed、keyshidden、keyssoft |
如果提供了 keysexposed 资源,但未提供 keyssoft 资源,那么只要系统已经启用软键盘,就会使用 keysexposed 资源,而不考虑键盘是否可见。如果用户打开硬键盘,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 另请参阅配置字段 hardKeyboardHidden和 keyboardHidden,这两个字段分别指示硬键盘的可见性和任何一种键盘(包括软键盘)的可见性。 |
主要文本输入法 | nokeys、qwerty、12key | nokeys:设备没有用于文本输入的硬按键。qwerty:设备具有标准硬键盘(无论是否对用户可见)。12key:设备具有 12 键硬键盘(无论是否对用户可见)。另请参阅 keyboard配置字段,该字段指示可用的主要文本输入法。 |
导航键可用性 | navexposed、navhidden | navexposed:导航键可供用户使用。navhidden:导航键不可用(例如,位于密封盖子后面)。如果用户显示导航键,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。另请参阅 navigationHidden,该字段指示导航键是否处于隐藏状态。 |
主要非触摸导航方法 | nonav、dpad、trackball、wheel | nonav:除了使用触摸屏以外,设备没有其他导航设施。dpad:设备具有用于导航的方向键。trackball:设备具有用于导航的轨迹球。wheel:设备具有用于导航的方向盘(不常见)。另请参阅 navigation,该字段指示可用的导航方法类型。 |
平台版本(API 级别) | v3、v4、v7 | 设备支持的 API 级别。例如,v1对应于 API 级别 1(带有 Android 1.0 或更高版本系统的设备),v4对应于 API 级别 4(带有 Android 1.6 或更高版本系统的设备)。如需了解有关这些值的详细信息,请参阅 Android API 级别文档。 |
限定符命名规则
- 你可以为单组资源指定多个限定符,并使用短划线分隔。例如,drawable-en-rUS-land
- 这些限定符必须遵循表中列出的顺序。例如
- 错误:drawable-hdpi-port/
- 正确:drawable-port-hdpi/
- 不能嵌套备用资源目录。例如:不能拥有 res/drawable/drawable-en/
- 值不区分大小写。在处理之前,资源编译器会将目录名称转换为小写,以避免不区分大小写的文件系统出现问题。 名称中使用的任何大写字母只是为了便于认读。
- 对于每种限定符类型,仅支持一个值。例如,若要对西班牙语和法语使用相同的可绘制对象文件,则您肯定不能拥有名为 drawable-rES-rFR/
的目录,而是需要两个包含相应文件的资源目录,如 drawable-rES/
和 drawable-rFR/。然而,实际上您无需将相同的文件都复制到这两个位置。相反,您可以创建指向资源的别名。 请参阅下面的创建别名资源。
创建别名资源
如果您想将某一资源用于多种设备配置(但是不想作为默认资源提供),则无需将同一资源放入多个备用资源目录中。 相反,您可以(在某些情况下)创建备用资源,充当保存在默认资源目录下的资源的别名。
注:并非所有资源都会提供相应机制让您创建指向其他资源的别名。 特别是,xml/
目录中的动画资源、菜单资源、原始资源以及其他未指定资源均不提供此功能。
例如,假设您有一个应用图标 icon.png,并且需要不同语言区域的独特版本。 但是,加拿大英语和加拿大法语这两种语言区域需要使用同一版本。 您可能会认为需要将相同的图像复制到加拿大英语和加拿大法语对应的资源目录中,但事实并非如此。 相反,您可以将用于二者的图像另存为 icon_ca.png(除 icon.png以外的任何名称),并将其放入默认 res/drawable/目录中。然后,在 res/drawable-en-rCA/和 res/drawable-fr-rCA/中创建 icon.xml文件,使用
可绘制对象
要创建指向现有可绘制对象的别名,请使用
定义字符串资源
对于要在应用程序中显示的每一段文本(即按钮的标签或TextView的文本),应首先在res/values/strings.xml
中定义文本。每条都有一个键(表示文本的id)和一个值(文本本身)。例如,如果我想要一个按钮显示“提交”,我可以在res/values/strings.xml
添加以下字符串资源:
Hello!
Submit
如果将此文件另存为 icon.xml(例如,在备用资源目录中,另存为 res/drawable-en-rCA/
),则会编译到可作为 R.drawable.icon引用的资源中,但实际上它是 R.drawable.icon_ca资源(保存在 res/drawable/中)的别名。
布局
要创建指向现有布局的别名,请使用包装在
如果将此文件另存为 main.xml,则会编译到可作为 R.layout.main引用的资源中,但实际上它是 R.layout.main_ltr 资源的别名。
利用资源提供最佳设备兼容性
要使应用支持多种设备配置,则务必为应用使用的每种资源类型提供默认资源。
例如,如果应用支持多种语言,请始终包含不带语言和区域限定符的 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 中选择可绘制对象。
系统使用以下逻辑决定要使用的资源:
- 淘汰与设备配置冲突的资源文件。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)中(下一个)优先级最高的限定符。(先从 MCC 开始,然后下移。)
-
是否有资源目录包括此限定符?
- 若无,请返回到第 2 步,看看下一个限定符。(在该示例中,除非达到语言限定符,否则答案始终为“否”。)
- 若有,请继续执行第 4 步。
淘汰不含此限定符的资源目录。在该示例中,系统会淘汰所有不含语言限定符的目录。
drawable/
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
例外:如果涉及的限定符是屏幕像素密度,则 Android 会选择最接近设备屏幕密度的选项。通常,Android 倾向于缩小大型原始图像,而不是放大小型原始图像。请参阅支持多种屏幕。
- 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止。在此示例中,屏幕方向是下一个判断是否匹配的限定符。因此,未指定屏幕方向的资源被淘汰:
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
剩下的目录是 drawable-en-port 。
尽管对所请求的每个资源均执行此程序,但是系统仍会对某些方面做进一步优化。 例如,系统一旦知道设备配置,即会淘汰可能永远无法匹配的备用资源。 比如说,如果配置语言是英语(“en”),则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中(不过,仍会将不带语言限定符的资源目录包含在该池中)。
注:限定符的优先顺序(表 2 中)比与设备完全匹配的限定符数量更加重要。**例如,在上面的第 4 步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(方向、触摸屏类型和输入法),而 drawable-en
只有一个匹配参数(语言)。但是,语言的优先顺序高于其他两个限定符,因此drawable-port-notouch-12key
被淘汰。
定义和访问资源
你还可以创建符合Android国际化标准的XML文件(),以便于在不同国家或设备之间更改此值。除此之外,我们还可以通过使用CDATA来转义字符串来存储更复杂的字符串(带有html或特殊字符),例如:
let us know
if you have feedback on this or if you would like to log in with another identity service. Thanks! This is a longer string! ]]>
访问资源
通过引用资源ID来访问该资源。所有资源ID都在项目的R类定义,后者由aapt工具自动生成。
编译应用时,aapt会生成R类,其中包含res/
目录中所有资源的资源ID。
资源ID始终由以下部分组成:
- 资源类型:每个资源都被分到一个“类型”组中,例如string、drawable和layout。资源类型
- 资源名称:它是不包括扩展名的文件名
在布局XML文件中引用
使用@
符号来访问资源
在Java文件中引用
使用getResources.getString
或·getString·方法来访问资源
String submitText = getResources().getString(R.string.submit_label)
其他资源例如图片、颜色等和字符串的引用类似。
定义颜色资源
#FFFFFF
#FFFF00
#FF00FF
在Java文件中引用颜色资源
// getResources().getColor() is now deprecated
// Resources res = getResources();
// int color = res.getColor(R.color.yellow);
// Use ContextCompatResources instead of getColor()
int color = ContextCompat.getColor(context, R.color.yellow);
注意:访问颜色资源的最新方式(从API 24开始),需要提供Context以解析任何自定义主题属性。更多有关颜色资源
在XML文件中引用颜色资源
与字符串资源类似,都是使用@
符号来访问资源
定义尺寸资源
25dp
150dp
30dp
16sp
在Java文件中引用尺寸资源
Resources res = getResources();
float fontSize = res.getDimension(R.dimen.font_size);
在XML文件中引用尺寸资源
与字符串资源类似,都是使用@
符号来访问资源
动态资源检索
在某些情况下,你可能希望使用键名称而不是通过资源ID来动态检索资源。例如,假设我想单独根据键名检索“submit_label”字符串。可以通过在Activity中使用getIdentifier()
方法来实现:
public String getStringValue(String key) {
// Retrieve the resource id String
packageName = getBaseContext().getPackageName();
Resources resources = getBaseContext().getResources();
int stringId = resources.getIdentifier(key, "string", packageName);
if (stringId == 0) {
return null;
}
// Return the string value based on the res id
return resources.getString(stringId);
}
类似地,也可以对其他类型的资源检索。例如,动态检索ID:
// getViewById("tvTest");
public String getStringValue(String key) {
// Retrieve the resource id
String packageName = getBaseContext().getPackageName();
Resources resources = getBaseContext().getResources();
int viewId = resources.getIdentifier(id, "id", packageName);
if (viewId == 0) {
return null;
}
// Return the string value based on the res id
return findViewById(viewId);
}
提供备用资源
几乎每个应用都应提供备用资源以支持特定的设备配置。例如,对于不同的屏幕密度和语言,你应分别包括备用可绘制对象资源和备用字符串资源。在运行时,Android会检测当前设备配置并为应用加载合适的资源。
- 不同外观尺寸的替代布局文件(例如手机和平板)
- 不同语言的替代字符串资源(例如英语和意大利语)
- 不同屏幕密度的图像资源
- 不同平台版本的替代样式资源(例如Holo和Material)
- 不同屏幕方向的替代布局文件(例如横屏和竖屏)
我们在res中以[resource]-[qualifiers]的形式创建一个新目录。例如,为多个屏幕密度提供所有图像
Android的限定符
配置 | 示例 | 描述 |
---|---|---|
语言 | en,fr | 在设备上选择的语言 |
屏幕大小 | sw480dp,sw600dp | 屏幕高度或宽度的最小值 |
屏幕方向 | port,land | 屏幕是横屏还是竖屏 |
屏幕密度 | hdpi,xhdpi | 屏幕密度通常用于图像 |
平台版本 | v7,v11,v21 | 平台版本通常用于样式 |
你可以为单个资源集指定多个限定符,以段划线分隔。例如,drawable-en-sw600dp-land适用于选择了英语环境且横屏的平板。注意,如果对资源目录使用多个限定符,则必须按照上表列出的顺序将它们添加到目录名称。参考官方文档
创建替代资源
总而言之,您可以创建不同情况所需的资源文件,然后由系统自动选择资源文件的最合适版本。
在运行时决定你的配置
当应用程序运行时,我们可以通过Activity或Context对象内的getResources().getConfiguration()
方法来检查当前配置(方向、屏幕大小等)。例如,要确定Activity的方向(纵向或横向 ):
String image;
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
image = "image_portrait.png";
// ...
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
image = "image_landscape.png";
// ...
}
可替换的文件
通常,指定不同的可替换布局文件来区分手机和平板。这个可以使用sw“最小宽度”限定符来完成。文件夹结构必须设置成如下的形式:
res/
layout/
activity_main.xml
item_photo.xml
layout-sw600dp/
activity_main.xml
layout-sw600dp-land/
activity_main.xml
layout-sw720dp/
activity_main.xml
item_photo.xml
layout-land/
activity_main.xml
item_photo.xml
一般来说,手机和平板在sw240和sw480之间。nexus 7是sw600,nexus10是sw720。你还可以简单地添加限定符(如layout-land)以适配所有横屏模式的设备。例如:
最佳布局实践
确保你的应用程序能否完美适配
- 避免在应用程序的代码中使用像素值
- 正确使用RelativeLayout,一定不要使用AbsoluteLayout
- 指定尺寸时要使用wrap_content、match_parent或dp
- 需要实现响应式设计时,使用layouts和drawables的可替换资源文件