文章目录
概览
资源是指代码使用的附加文件和静态内容,例如位图、布局定义、界面字符串、动画说明等。
应始终外部化应用资源(例如图像和代码中的字符串),以便单独对其进行维护。此外,还应该为特定的设备配置提供备用资源,方法是将其进行分组并放入专门命名的资源目录中。在运行时,android会根据当前配置使用合适的资源。例如,可能需根据屏幕尺寸提供不同的界面布局,或根据语言设置提供不同的字符串。
外部化应用资源后,便可以使用在项目R
类中生成的资源id来访问这些资源。
分组资源类型
应将各类资源放入项目
res/
目录的特定子目录中。例如,以下是一个简单项目的文件层次结构:
MyProject/
src/
MyActivity.java
res/
drawable/
graphic.png
layout/
main.xml
info.xml
mipmap/
icon.png
values/
strings.xml
示例中,
res/
目录包含所有资源(在子目录中):图像、布局、启动器图标的mipmap/
目录以及一个字符串资源文件。资源目录名称非常重要:
目录 | 资源类型 |
---|---|
animator/ | 用于定义属性动画的XML文件。 |
anim/ | 用于定义渐变动画的XML文件(属性动画也可保存在此目录中,但为了区分这两种类型,属性动画首选animator/ 目录)。 |
color/ | 用于定义颜色状态列表的XML文件。请参阅颜色状态列表资源。 |
drawable/ | 位图文件(.png 、.9.png 、.jpg 、.gif )或者是那些可以编译为以下可绘制对象资源子类型的XML文件:位图文件 九宫格(nine-patches,可调整大小的位图) 状态列表 形状 动画可绘制对象 其他可绘制对象等 具体请参阅Drawable资源 |
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/ 目录中的文件描述多个资源。对于这个目录中的文件,元素的每一个子元素都定义了一个资源。例如, <string> 元素创建一个R.string 资源,<color> 元素创建一个R.color 资源。因为每个资源都是用自己的XML元素定义的,所以可以根据需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,推荐在不同的文件中放置唯一的资源类型。例如,下面是一些你可以在这个目录下创建的资源的文件名约定: arrays.xml :资源数组(类型数组)colors.xml :颜色值dimens.xml :尺寸值strings.xml :字符串值styles.xml :样式请参阅字符串资源、样式资源和更多资源类型。 |
xml/ | 可在运行时通过调用Resources.getXML()读取的任意XML文件。各种XML配置文件(如可搜索配置)都必须保存在此处。 |
font/ | 带有扩展名的字体文件(如.ttf 、.otf 或.ttc ),或包含<font-family> 元素的XML文件。如需详细了解作为资源的字体,请参阅XML中的字体。 |
注意:切勿将资源文件直接保存在
res/
目录内,因为这样会造成编译错误。
上述定义的子目录中,保存的资源为默认资源。即,这些资源定义应用的默认设计和内容。然而,不同类型的android设备可能需要不同类型的资源。例如,如果设备屏幕比标准屏幕大,则应提供不同的布局资源,从而充分利用额外的屏幕空间。或者,如果设备的语言设置不同,则应提供不同的字符串资源,以便将界面中的文本转换为相应的语言。如要为不同设备配置提供这些不同资源,除默认资源以外,还需提供备用资源。
提供备用资源
几乎每个应用都应提供备用资源,以便支持特定的设备配置。例如,对于不同的屏幕分辨率和语言,应该分别加入备用的可绘制对象资源和备用的字符串资源。在运行时,android会检测当前设备配置并为应用加载合适的资源。
要为一组资源指定特定于配置的备选方案,请执行以下操作:
举个栗子:
res/
drawable/
icon.png
background.png
drawable-hdpi/
icon.png
background.png
hdpi
限定符表示该目录中的资源适用于屏幕分辨率较高的设备。其中,每个可绘制对象目录中的图像均已针对特定的屏幕分辨率调整了大小,但文件名完全相同。如此一来,用于引用icon.png
或background.png
图像的资源id始终相同,但android会通过将设备配置信息与资源目录名称中的限定符进行比较,选择最符合当前设备的各个资源版本。如果没有与特定设备配置相匹配的备用资源,则android会使用相应的默认资源(不含配置限定符)。
限定符命名规则
以下是一些关于使用配置限定符名称的规则:
- 单组资源可以指定多个限定符,并使用短划线分隔。例如,
drawable-en-rUS-land
适用于屏幕方向为横向的美国英语设备。- 这些限定符必须遵循官方文档中列出的顺序。
- 不能嵌套备用资源目录。例如,不能为
res/drawable/drawable-en/
。- 值不区分大小写。在处理之前,资源编译器会将目录名称转换为小写,以免不区分大小写的文件系统出现问题。名称中使用的所有大写字母只是为了便于认读。
- 每种限定符类型仅支持一个值。例如,若要对西班牙语和法语使用相同的可绘制对象文件,则不能拥有名为
drawable-rES-rFR/
的目录,而是需要两个包含相应文件的资源目录,如drawable-rES/
和drawable-rFR/
。然而,实际无需在两处复制相同的文件。相反,可以创建指向资源的别名。请参阅下面的创建别名资源。
创建别名资源
如果想将某一资源用于多种设备配置(但不想以默认资源的形式提供该资源),则无需将同一资源放入多个备用资源目录中。相反,可以(在某些情况下)创建备用资源,充当默认资源目录中所保存资源的别名。
例如,假设有一个应用图标icon.png
,并且需要用于不同语言区域的独特版本。但是,加拿大英语和加拿大法语这两种语言区域需使用同一版本。你可能会认为,需要将相同图像复制到加拿大英语和加拿大法语所对应的资源目录中,但事实并非如此。相反,可以将用于二者的图像保存为icon_ca.png
(除icon.png
以外的任何名称),并将其放入默认的res/drawable/
目录中。然后,在res/drawable-en-rCA/
和res/drawable-fr-rCA/
中创建icon.xml
文件,使用<bitmap>
元素引用icon_ca.png
资源。这样,只需存储PNG文件的一个版本和两个指向该版本的小型XML
文件。
请注意:并非所有资源都会提供相应的机制。特别是,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/
目录中)。
布局
如要创建指向现有布局的别名,请使用
<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
子类(例如,R.drawable
对应所有可绘制对象的资源),而该类型的每个资源都有对应的静态整型数值(例如,R.drawable.icon
)。该整型数值就是可用来检索资源的资源id。
尽管资源id是在R
类中指定的,但是完全不需要在该类中查找资源id。资源id始终由以下部分组成:
- 资源类型:每个资源都被分到一个类型组中,例如
string
、drawable
和layout
。如需了解有关不同类型的详细信息,请参阅资源类型。- 资源名称,可以是不包括扩展名的文件名称;如果资源是一个简单的值(如字符串),则可以是XML文件中
android:name
属性中的值。
访问资源的方法有两种:
- 在代码中:使用来自
R
类子类的静态整型数,例如:R.string.hello
。string
为资源类型,hello
为资源名称。- 在XML中:使用同样与
R
类中所定义资源ID
对应的特殊XML
语法,例如:@string/hello
。
在代码中访问资源
通过将资源id作为方法参数传递,可以在代码中使用资源。例如,可以通过使用
setImageResource()
来设置一个ImageView
来使用res/drawable/myimage.png
资源:
ImageView imageView = (ImageView) findViewById(R.id.myimageview);
imageView.setImageResource(R.drawable.myimage);
还可以利用
Resources
中的方法检索个别资源,并且可以通过getResources()
获得该资源的实例。
语法
以下是在代码中引用资源的语法:
[<package_name>.]R.<resource_type>.<resource_name>
<package_name>
是资源所在包的名称(如果引用的资源来自于自己的包,则不需要)。<resource_type>
是R
的子类。<resource_name>
是不带扩展名的资源文件名,或XML中的android:name
属性值(若资源是简单值)。
用例
许多方法可以接受资源id作为参数,同时可以利用
Resources
中的方法检索资源。你可以通过Context.getResources()
获得Resources
的实例。
以下是一些在代码中访问资源的示例:
// 从一个可绘制的资源加载当前屏幕的背景
getWindow().setBackgroundDrawableResource(R.drawable.my_background_image) ;
// 通过从资源对象获得一个字符串来设置Activity的标题,因为这个方法需要一个CharSequence而不是一个资源id。
getWindow().setTitle(getResources().getText(R.string.main_title));
// 给当前屏幕加载一个自定义的布局
setContentView(R.layout.main_screen);
// 通过从资源对象获取动画来设置幻灯片动画
flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.hyperspace_in));
// 使用资源id设置TextView对象的文本
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" />
请注意:应始终使用字符串资源,以便将应用本地化为其他语言。
引用样式属性
利用样式属性资源,可以在当前应用的主题背景中引用某个属性的值。引用样式属性使你在自定义界面元素的外观时,无需采用提供硬编码值的方式,可以通过为其设置样式,以匹配当前主题背景提供的标准变体来达成目的。引用样式属性的实质作用是,在当前主题背景中使用此属性定义的样式。
如要引用样式属性,名称语法几乎与普通资源格式完全相同,区别在于需要将at符号(@
)改为问号(?
),并且资源类型部分为可选项。例如:
?[<package_name>:][<resource_type>/]<resource_name>
访问原始文件
尽管并不常见,但的确有可能需要访问原始的文件和目录。如果确有需要,则将文件保存在
res/
中并没有用,因为从res/
读取资源的唯一方法是使用资源id。相反,可以改为将资源保存在assets/
目录中。
保存在assets/
目录中的文件没有给定资源id,因此不能通过R
类或从XML资源引用它们。相反,可以像普通文件系统一样查询assets/
目录中的文件,并使用AssetManager
读取原始数据。
但是,如果所需要的只是读取原始数据(例如视频或音频文件)的能力,那么将文件保存在res/raw/
目录中,并使用openRawResource()
读取字节流。
访问平台资源
Android包含许多标准资源,例如样式、主题和布局。要访问这些资源,请使用
android
包名限定资源的引用。例如,android提供了一个布局资源,可以用作ListAdapter
中的列表项:
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myarray));
在这个例子中,
simple_list_item_1
是平台为ListView
中的项目所定义的布局资源。可以使用该资源,而不必自行创建列表项布局。
利用资源提供最佳设备兼容性
为使应用支持多种设备配置,请务必为应用使用的每种资源类型提供默认资源,这一点非常重要。不仅是因为应用可能会在超出预期的配置上运行,也因为新版android有时会添加旧版本不支持的配置限定符。如果使用新的资源限定符,但希望维持对旧版 本的代码兼容性,则当旧版本的android运行应用时,应用将在无默认资源的情况下崩溃,因为此时它无法使用以新限定符命名的资源。
这条规则有一个例外:如果应用的minSdkVersion
为4或更高版本,则在提供带屏幕密度限定符的备用可绘制对象资源时,不需要默认可绘制的对象资源。即使没有默认的可绘制对象资源,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/ 未被淘汰,因为此时每个屏幕密度均视为匹配。如需了解详细信息,请参阅支持多种屏幕文档。
- 选择官方文档中(下一个)优先级最高的限定符(从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
),则系统绝不会将语言限定符设置为非英语的任何资源目录加入选中的资源池(不过,仍会将不带语言限定符的资源目录加入到该池中)。
在根据屏幕尺寸限定符选择资源时,如果没有更好的匹配资源,则系统将使用为比当前屏幕更小的屏幕设计的资源(例如,必要时,大尺寸屏幕将使用标准尺寸的屏幕资源)。但是,如果唯一可用的资源大于当前屏幕,则系统不会使用这些资源,并且如果没有其他资源与设备配置匹配,应用将会崩溃(例如,如果所有布局资源均用xlarge
限定符标记,但设备是标准尺寸的屏幕)。
请注意:限定符的优先级(官方文档中)比与设备完全匹配的限定符数量更加重要。例如,在上面的第4步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(屏幕方向、触摸屏类型和输入法),而drawable-en
只有一个匹配参数(语言)。但是,语言的优先级高于其他两个限定符,因此系统会淘汰drawable-port-notouch-12key
。