原文:Understanding App Resources
—Understanding Strings and Resources
概述
在Android中,几乎一切事物都是资源。定义在应用程序中可访问的资源,是Android开发的一个重要部分。
资源用于定义颜色、图像、布局、菜单和字符串值。这样做的意义是可以使不良的“硬编码”习惯消失。所有内容分别定义在这些资源文件中,可以被应用程序中的代码引用。这些资源最简单和最常见的用法是使用字符串资源,实现灵活的本地化文本。
资源类型
以下是Android应用中最常用的资源类型:
Name | Folder | Description |
---|---|---|
Property Animations(属性动画) | animator | 定义属性动画的XML文件 |
Tween Animations(补间动画) | anim | 定义补间动画的XML文件 |
Drawables | drawable | 位图文件或作为图像的XML文件 |
Layout | layout | 定义用户接口布局的XML文件 |
Menu | menu | 定义菜单或动作栏的XML文件 |
Values | values | 使用string,integer或color的XML文件 |
另外,注意以下定义在values
文件夹下的关键文件:
Name | File | Description |
---|---|---|
Colors | res/values/colors.xml |
颜色定义,例如文本颜色 |
Dimensions | res/values/dimens.xml |
尺寸值,例如内边距 |
Strings | res/values/strings.xml |
字符串值,例如文本标题 |
Styles | res/values/styles.xml |
样式值,例如AppBar的颜色 |
想了解资源类型的完整列表,请参考Providing a Resource指南。
为应用提供资源
定义字符串资源
对于你要在应用中展示的每一段文本(例如按钮的标签或TextView上的文字),你应该先将文本定义在res/values/strings.xml
文件中。每个条目包含一个“键”(代表文本的标识)和一个“值”(文本本身)。例如,如果你想在按钮上展示“Submit”,你应该想如下的字符串资源添加到res/values/strings.xml
文件中:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello!</string>
<string name="submit_label">Submit</string>
</resources>
现在我如果引用了submit_label
字符串资源,默认地将会显示“Submit”。稍后,你可以创建全匹配的资源文件,以针对不同的系统语言或设备更改此值。我们还可以使用CDATA来转义字符串,以存储更复杂的字符串(带有html标签或特殊字符),例如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="feedback_label">
<![CDATA[
Please <a href="http://highlight.com">let us know</a> if you have feedback on this or if
you would like to log in with another identity service. Thanks! This is a longer string!
]]>
</string>
</resources>
对于字符串资源定义的更多细节,请参考本篇指南。你还可以参考指南中的样式资源和其他资源类型。
在应用中引用资源
既然我们已经定义好了自己的字符串资源,我们就能够在Java代码或者XML布局文件中访问这些资源。要在XML布局文件中访问资源,要使用@语法:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/submit_label" />
要在Java代码中直接访问资源,需要使用getResources.getString
或 getString
方法,访问给定资源ID的值:
String submitText = getResources().getString(R.string.submit_label)
字符串值将会被检索。其他资源类型也采用了类似的工作机制,比如Drawable和Color等。getResourses()
方法返回一个包含许多资源提取方法的Resources对象。每一种资源定义在res
目录下的不同文件夹和文件中,这由它们的具体类型所决定。
定义颜色资源
除了上边展示的字符串资源,还有以下常见的资源类型。首先,让我们看一下用于定义整个应用中所有颜色的颜色资源文件。颜色资源在res/values/colors.xml
中定义,XML文件的样式如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="yellow">#FFFF00</color>
<color name="fuchsia">#FF00FF</color>
</resources>
颜色资源可以在Java代码中访问:
// getResources().getColor():该方法目前被废弃
// Resources res = getResources();
// int color = res.getColor(R.color.yellow);
// 使用ContextCompatResources
int color = ContextCompat.getColor(context, R.color.yellow);
值得注意的是,当前最新的访问颜色资源的方式(自从API 24开始),要求提供context来解析自定义的Theme属性。参阅这篇文章了解更多的相关内容。
并且在XML布局中的任意View引用颜色资源如下所示:
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@color/fuchsia"
android:text="Hello"/>
&esmp;这是关于颜色资源你所要知道的全部内容,一定不要在布局文件中使用“硬编码”的颜色值。
定义尺寸资源
接下来,我们来看一下用于定义整个应用中所有尺寸大小的尺寸资源文件。一个尺寸由一个数字后边跟测量单位来指定。例如10px
,5sp
。尺寸资源在res/values/dimens.xml
文件中定义,XML文件中的样式如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="textview_height">25dp</dimen>
<dimen name="textview_width">150dp</dimen>
<dimen name="ball_radius">30dp</dimen>
<dimen name="font_size">16sp</dimen>
</resources>
尺寸资源可以在Java代码中访问:
Resources res = getResources();
float fontSize = res.getDimension(R.dimen.font_size);
并且在XML布局中的任意View引用尺寸资源如下所示:
<TextView
android:layout_height="@dimen/textview_height"
android:layout_width="@dimen/textview_width"
android:textSize="@dimen/font_size"/>
这是关于尺寸资源你所要了解的全部内容。一定要以这种方式定义字体大小、内间距和外边距值,避免“硬编码”方式出现。这是有关的其他资源类型。
动态资源检索
在某些情况下,你可能想通过键名称而非“硬编码”的资源ID来动态检索资源。例如,假设我想通过单独的键名称来检索“submit_label”字符串,就可以通过Activity中的getIdentifier
方法实现:
public String getStringValue(String key) {
//检索资源ID
String packageName = getBaseContext().getPackageName();
Resources resources = getBaseContext().getResources();
int stringId = resources.getIdentifier(key, "string", packageName);
if (stringId == 0) { return null; }
//基于资源ID返回字符串值
return resources.getString(stringId);
}
现在你就可以动态的引用字符串资源了:
public String myKey = "submit_label"; // 映射到R.string.submit_label
public String myStringValue = getStringValue(myKey); // Returns string text
类似的方法也能被用在其他类型的资源上。例如,通过字符串类型的ID动态检索View:
// getViewById("tvTest");
public View getViewById(String id) {
//检索资源ID
String packageName = getBaseContext().getPackageName();
Resources resources = getBaseContext().getResources();
int viewId = resources.getIdentifier(id, "id", packageName);
if (viewId == 0) { return null; }
return findViewById(viewId);
}
查看getResources对象和<a href="http://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)">getIdentifier</a>获取更多详细信息。
提供可替代的资源
响应式设计
为了实现出色的UI设计,对于Android开发者来说,创建可在多种类型的备上正常工作的应用是非常重要的。为实现这个目的,我们首先要根据屏幕尺寸将Android设备分为以下不同的种类:
应用必须设计成可在多种不同的屏幕密度和屏幕尺寸上正常运行。这可借助于Android框架提供的各种系统实现。
介绍可替代资源
开发者能够使用的强大开发工具之一是,可根据特定的限定符如手机尺寸,系统语言,屏幕密度等提供“替代资源”。替代资源的常见用途包括:
- 用于不同外形设备的替代布局文件(如手机VS平板)
- 用于不同系统语言的替代字符串资源(如英语VS意大利语)
- 用于不同屏幕密度的替代图像资源
- 用于不同平台版本的替代样式资源(Holo VS Material)
- 用于不同屏幕方向的替代布局文件(portrait VS landscape)
要为一组资源指定基于特定配置下的替代资源,我们在res
中以[资源类型]-[限定符]
的形式创建一个新的目录。一个最佳的实践是确保为多种密度的屏幕提供了所有可适配的图像。
这是通过包含具有相同图像不同版本的res/drawable-hdpi
, res/drawable-xhdpi
, and res/drawable-xxhdpi
文件夹实现的。正确的资源会根据设备的屏幕密度被系统自动选中。目录的列表展开可能会如下所示:
res/
drawable/
icon.png
background.png
drawable-hdpi/
icon.png
background.png
注意所有不同文件夹下的资源文件都要有相同的名字。该系统适用于具有限定符的任意类型的资源。
理解限定符
Android支持多种限定符的配置,并且可以使用短线分隔限定符的方式,将多个限定符添加到一个目录名称。常用的限定符如下所示:
Configuration | Examples | Description |
---|---|---|
Language |
en , fr
|
设备上选中的语言代码 |
Screen size |
sw480dp ,sw600dp
|
屏幕高度或者宽度的最小宽度 |
Screen orientation |
port , land
|
屏幕是出于横屏或竖屏模式 |
Screen density |
hdpi , xhdpi
|
常用于可替代的图片 |
Platform version |
v7 , v11 , v21
|
常用于样式 |
你可以为单独的一组资源,使用短线分隔方式指定多个限定符。例如,drawable-en-sw600dp-land
用于英文系统的横屏模式下的平板设备。注意如果你对一个资源目录使用了多个限定符,在将它们添加到目录名中时一定要按照上表中所列出的顺序。参阅完整限定符集的官方文档。
创建可替代资源
在Android Studio中,创建可替代资源最简单的方式是在Android项目边栏中的资源子目录上右击(例如layout),使用New => Layout resource file
方法指定你期望的限定符(例如orientation
):
这将会创建两个版本的布局文件,一个用于竖屏模型,一个用于横屏模式。如果你为第二个版本的布局文件添加了不同的标签,在屏幕方向旋转时你将发现这个效果会自动触发:
总结一下,你可以创建适用于不同情景下的多个资源文件版本,最合适的版本会被系统自动选中使用。
在运行时确定配置
当应用正在运行时,我们可以通过Activity或Context对象的getResources().getConfiguration()
方法访问Configuration对象来检测当前的配置(方向,屏幕尺寸等)。例如,想要确定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";
// ...
}
类似地我们可通过访问一个Context对象在任何对象中访问它。例如,在ArrayAdapter
中使用getContext().getResources().getConfiguration()
方法获取配置。
备用布局文件
通常,替代资源用于为手机和平板指定不同的布局文件。这可以通过使用“最小宽度”限定符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
之间。7英寸平板为sw600
,10英寸平板为sw720
。你也可以简单地添加限定符如layout-land
,以横屏模式应用于所有设备。这是针对上述说明的一个例子:
有关如何管理平板设备的响应式布局的指南,请参阅灵活的用户接口
指南。你也可以参阅这篇文章UI设计最佳实践和有关资源的官方文档 获取更多细节信息。
最佳布局实践
这有一个快速检查清单,可确保你的应用可以在不同的屏幕上正常展示:
- 避免在应用代码中使用硬编码的像素值
- 合理使用
RelativeLayout
,绝不使用AbsoluteLayout
- 指定尺寸值时,使用
wrap_content
,match_parent
, 或dp
单位 - 为确保响应式的设计,使用替代布局和图像资源
在官方指南上查看屏幕独立性的其他最佳实践。
资源别名
当你想在多个设备配置中使用同一个资源时,你不必将同一个资源文件在替代资源文件夹中拷贝多份。相反,你可以创建替代资源,作为保存在默认资源目录中的资源别名。
最佳资源匹配
当你请求替代资源时,Android会基于当前设备的配置在运行时选择替代资源。参阅 官方资源指南了解匹配资源如何被选中的详细概述。
参考引用
- http://developer.android.com/guide/topics/resources/string-resource.html
- http://developer.android.com/guide/topics/resources/accessing-resources.html
- http://mobile.tutsplus.com/tutorials/android/android-string/
- http://developer.android.com/guide/topics/resources/providing-resources.html
- http://developer.android.com/training/multiscreen/screendensities.html
- http://www.evoketechnologies.com/blog/effective-ui-design-tips-android-devices/
- http://www.androiddesignpatterns.com/2016/08/contextcompat-getcolor-getdrawable.html/