善于利用 Android 资源






4.98/5 (26投票s)
精通和胜任 Android 资源管理。
![]() |
点击此处观看演示视频
|
引言
每个 Android 应用的背后,都有提供功能的代码以及支持这些功能的资源。资源是指任何可以数字化用于代码中的东西,例如文本字符串、布局、动画、图形、颜色以及您的 Android 应用所需的任何其他静态数据。Android 的最佳实践倡导“**关注点分离**”的原则——将代码及其资源分离。在这方面,Android 框架提供了一种系统化的方式来实现这一原则。您可能编写出色的代码,但资源管理不当会严重影响应用的性能。因此,对于每个 Android 开发者来说,了解如何有效率地管理 Android 应用中的资源至关重要。
关注点分离
为什么要将代码及其资源分离?我敢打赌你会问这个问题。原因有三:*可重用性*、*可维护性*和*替代方案*。
-
**可重用性**:为特定用途创建资源,并在应用中任何需要相同用途的地方重复使用,可以减少开销。例如,与其在应用中硬编码所有按钮的标题都取相同的值“Enter”,不如创建一个名为“Enter”的字符串资源,然后让每个按钮都引用这个字符串资源。
-
**可维护性**:当每个用途只有一个资源时,当需要时,您可以一次性更改资源值,而无需更改代码。例如,您可以将前一个示例中的字符串资源值从“Enter”更改为“Submit”,并且此更改将立即应用于应用中的所有按钮,根本无需修改代码。这难道不棒吗!
- **替代方案**:鉴于设备配置的多样性,例如屏幕密度、方向和语言,没有“一刀切”的资源。通过将资源与代码分离,您能够为不同的配置提供替代资源,而无需修改代码。
所以,最重要的是“**不要动代码**”。
言出必行
学习如何管理 Android 资源的最好方法莫过于亲自动手尝试。我们将构建一个尽可能涉及多种资源类型的简单应用,以便您获得处理它们的第一手经验。换句话说,我们将言出必行。
在尝试之前,您应该已经阅读过我之前的文章——创建简单的 Hello World Android 项目和Android UI 布局和控件,因为我不会重复诸如如何在 Android Studio 中创建 Activity 等细节。
准备工作
-
启动您的 Android Studio。
-
创建一个名为“*AndroidResources*”的新项目(图 1)。
图 1:创建新项目 -
创建一个名为“*MainActivity*”的新 Activity(图 2)。
图 2:创建新 Activity
字符串资源
“*MainActivity*”的 XML 布局文件名为“activity_main.xml”。它带有一个默认的 *RelativeLayout* 布局和一个显示“Hello world!”的默认 *TextView*(图 3)。
![]() |
图 3:MainActivity
|
您将修改此 *TextView* 如下:
-
在文本视图中打开“activity_main.xml”,*<TextView>* 节点应如下所示
<TextView android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" />
如果 android:text 属性显示“Hello World!”而不是“@string/hello_world”,只需双击它。
-
“Hello World!”并非硬编码,它是从名为“hello_world”的字符串资源中引用的。在哪里可以找到它?它位于 **res/values/** 目录下名为“strings.xml”的文件中(图 4)。
图 4:strings.xml -
对于您在“strings.xml”中创建的每个字符串资源,Android 构建器将在“**R.java**”内部的 **string** 类中创建一个新的静态整数,以便您可以通过“**R.string.hello**”在代码中引用它。如果您想查看“R.java”,它位于此处:(图 5)
图 5:strings.xml -
您可以在“strings.xml”中将“hello_world”元素的值从“Hello World!”更改为“Hello, Code Project!”,并立即看到效果(图 6)。
<string name="hello_world">Hello, Code Project!</string> 图 6:已更改的值 -
我可以动态设置 *TextView* 的值吗,我的意思是运行时通过代码设置?当然可以。但要做到这一点,您必须首先在“activity_main.xml”中为 *TextView* 的“***android:id***”属性赋值,以便在代码中引用它。您还将删除“android:text”属性,因为其值将在代码中赋值。
<TextView android:id="@+id/hello" android:layout_width="wrap_content" android:layout_height="wrap_content" />
“@+id/hello”中的 **+** 号是 Android 告知构建器在“**R.java**”内部的 **id** 类中创建一个新的静态整数的方式,以便您可以通过“**R.id.hello**”在代码中引用它。如果您再次查看“R.java”,您应该会在那里找到它(图 7)
图 7:R.java打开“MainActivity.java”并添加图 8 中突出显示的代码。第一行代码通过将“R.id.hello”传递给“findViewById()”方法来查找 *TextView*。第二行通过“R.string.hello_world”从字符串资源中获取字符串值。最后一行代码将其文本属性设置为字符串值。
图 8:MainActivity.java由于新的文本值仅在运行时生效,您必须在 AVD 或物理设备上测试它才能看到其效果(图 9)。
图 9:即时生效!
学习要点 1当您需要新的字符串值时,始终在“strings.xml”中创建一个字符串资源来保存该字符串值,示例如下
<resources> <string name="app_name">AndroidResources</string> <string name="hello_world">Hello, Android!</string> <string name="action_settings">Settings</string> </resources>每当您需要使用此值时,您将引用保存此值的字符串资源的名称。有两种方法可以访问字符串资源
- 在 XML 中,
@string/<name of string resource>例如,
@string/hello_world- 在代码中,
R.string.<name of string resource>例如,
R.string.hello_world
颜色资源
在 **res/values/** 目录下创建一个名为“colors.xml”的新资源文件(图 10)。“colors.xml”是用于存储颜色资源的默认资源文件。
![]() |
图 10:创建新的资源文件
|
在 *Text* 视图中打开“colors.xml”,并添加一个名为“customColor”的新颜色元素,其颜色值为“#421d49”。
<resources> <color name="customColor">#421d49</color> </resources>
尺寸资源
在 *Text* 视图中打开位于 **res/values/** 目录下的“dimens.xml”。“dimens.xml”是用于存储尺寸值的默认资源文件。添加一个名为“text_size”的新尺寸资源,其值为“30sp”,如下所示
<dimen name="text_size">30sp</dimen>
样式资源
在 *Text* 视图中打开位于 **res/values/** 目录下的“styles.xml”。“styles.xml”是用于存储样式和主题的默认资源文件。添加一个名为“customStyle”的新样式资源,其中包含六个项目,如下所示
<style name="customStyle"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:gravity">center_horizontal</item> <item name="android:textSize">@dimen/text_size</item> <item name="android:textColor">@color/customColor</item> <item name="android:typeface">serif</item> </style>
请注意
-
样式项的名称取自属性名称,例如“android:layout_width”和“android:gravity”。
-
其中两个项目,即“android:textSize”和“android:textColor”,是从您之前创建的“dimens.xml”和“colors.xml”各自的资源文件中分配值的。通常,您在 XML 中引用资源的方式如下
@resourceType/<name of resource>
将自定义样式应用于 TextView
让我们将您刚刚创建的自定义样式应用于“MainActivity”上的 *TextView*。
在 *Text* 视图中打开“activity_main.xml”,删除“android:layout_width”和“android:layout_height”属性,并添加 style 属性,如下所示
<TextView style="@style/customStyle" android:id="@+id/hello" />
在 AVD 上运行它,结果就呈现在您眼前(图 11)。
![]() |
图 11:创建新的资源文件
|
学习要点 2您可以从另一个资源文件引用资源。您已在包含颜色资源和尺寸资源的自定义样式资源中完成了此操作。通常,有两种方法可以引用资源,
- 在 XML 中,
@<resourceType>/<name of resource>例如,
@dimen/text_size- 在代码中,
R.<resourceType>.<name of resource>例如,
R.dimen.text_size
属性动画
Android 框架提供了属性动画系统,您可以通过随时间改变对象的属性来为任何对象设置动画。您可以从Android 开发者阅读有关属性动画如何工作的详细信息。让我们通过改变“MainActivity”上 *TextView* 的“alpha”属性值来制作一个简单的属性动画。
-
在“res/”目录下创建一个名为“animator”的新资源目录。“animator”目录是存储所有属性动画资源的默认目录。
-
在“animator”目录下,创建一个名为“property_animation.xml”的新资源文件,并添加以下内容
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator android:propertyName="alpha" android:duration="2000" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0.0" android:valueTo="1.0" /> </set>
此 XML 文件声明了一个属性动画模式,用于在 2 秒内将任何对象的“alpha”属性从完全透明变为完全不透明,然后无限次反向执行。
-
打开“MainActivity.java”,并添加图 12 中突出显示的代码。该代码将首先将 XML 资源膨胀到一个 AnimatorSet 对象,然后通过“setTarget()”方法将 *TextView* 设置为动画的目标对象,最后调用“start{}”方法启动动画。
图 12:激活属性动画的代码 -
在 AVD 上运行它,您应该会看到“Hello, Code Project!”反复淡入淡出。
视图动画
Android 框架提供了视图动画系统,您可以通过它对任何 View 执行补间动画。补间动画通过根据一组信息(如起点、终点、旋转等)计算中间帧,对 *View* 对象的内容执行一系列简单的变换(如移动、调整大小和旋转)。您可以从 Android Developers 阅读有关视图动画如何工作的详细信息。让我们制作一个简单的补间动画来旋转“MainActivity”上的图像。
-
在 *TextView* 下方,向“activity_main.xml”添加一个 ImageView,并将其图像源指定为默认的“ic_launcher”可绘制对象
,如下所示
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/robotImage" android:src="@drawable/ic_launcher" android:layout_below="@+id/hello" android:layout_centerHorizontal="true" android:layout_marginTop="107dp" />
-
图片看起来太小了,我们把它放大。我们该怎么做?使用尺寸资源。在“dimens.xml”中添加两个新的 <dimen> 元素,分别名为“image_height”和“image_width”,它们都取值 100dp,如下所示
<dimen name="image_height">100dp</dimen> <dimen name="image_width">100dp</dimen>
回到“activity_main.xml”并更改 ImageView 的“android:layout_width”和“android:layout:height”以指向您刚刚创建的两个新尺寸资源,如下所示
android:layout_width="@dimen/image_width" android:layout_height="@dimen/image_height"
现在图像应该看起来更大了,宽度和高度分别为 100dp。
-
在“res/”目录下创建一个名为“anim”的新资源目录。“anim”目录是存储所有 View 动画资源的默认目录。
-
在“anim”目录下,创建一个名为“tween_animation.xml”的新资源文件,并添加以下内容
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <rotate android:fromDegrees="0" android:toDegrees="360" android:repeatCount="infinite" android:repeatMode="restart" android:pivotX="50%" android:pivotY="50%" android:duration="2000" /> </set>
此 XML 文件定义了一个补间动画方案,用于在 2 秒内重复围绕其中心坐标旋转一个 View。
-
打开“MainActivity.java”,并添加图 13 中突出显示的代码。该代码将找到 ImageView,加载动画方案,然后应用动画方案来为 ImageView 设置动画(旋转)。
图 13:激活补间动画的代码 -
在 AVD 上运行它,您应该会看到 Android 机器人顺时针旋转,同时“Hello, Code Project!”反复淡入淡出(图 14)
图 14:动画正在运行!
ColorStateList
一个 **ColorStateList** 是一个对象,它定义了一个颜色列表,用于在不同状态下显示,例如 state_pressed、state_focused 和 neither。状态提供点击和选择的确认。它可以应用于 View 以根据 View 的状态更改其颜色。例如,一个 Button 可以处于这三种状态中的任何一种——按下、聚焦或两者都不是。使用 ColorStateList,您可以为每种状态提供不同的颜色。在每次状态更改期间,状态列表从上到下遍历,第一个匹配当前状态的项将被使用。因此,您应该将默认选择作为最后一项,以便在所有上述条件都不满足时将其选中。
-
在“res/”目录下创建一个名为“color”的新资源目录。“color”目录是存储所有 ColorStateList 资源的默认目录。
-
在“color”目录下,创建一个名为“button_state.xml”的新资源文件,并添加以下内容
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:color="#ff0000"/> <!-- pressed red --> <item android:state_focused="true" android:color="#0000ff"/> <!-- focused blue --> <item android:color="#000000"/> <!-- default black --> </selector>
此 XML 文件为 View 的每种状态定义了不同的颜色,“红色”表示按下,“蓝色”表示聚焦,“黑色”表示两者都不是。
-
向“strings.xml”添加一个字符串资源,如下所示
<string name="click_me">Click Me!</string>
-
在 Android 机器人下方,向“activity_main.xml”添加一个按钮,如下所示
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/click_me" android:textColor="@color/button_state" android:id="@+id/button" android:layout_alignParentBottom="true" android:layout_alignRight="@+id/robotImage" android:layout_alignEnd="@+id/robotImage" android:layout_marginBottom="109dp" />
请注意,我为“android:text”属性使用了名为“click_me”的字符串资源。您应该已经能够创建它。主要关注点是 **android:textColor** 属性,它指向您刚刚创建的“button_state.xml”。通过这种方式,按钮将根据“button_state.xml”中的 ColorStateList 在不同状态下呈现不同的颜色。
- 我们已经完成了 ColorStateList 的部分,在您的 AVD 上测试一下吧。
Menu
我们将在“MainActivity”的动作栏中添加两个菜单项。所有菜单资源的默认目录是“res/menu”。
- 首先,在“strings.xml”中添加两个新的字符串资源,用于稍后标记这两个菜单项,如下所示
<string name="action_file">File</string> <string name="action_help">Help</string>
-
在“menu”目录下,应该有一个默认的“main.xml”文件,否则就创建一个。覆盖其内容如下
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_file" android:title="@string/action_file" android:showAsAction="never" /> <item android:id="@+id/action_help" android:title="@string/action_help" android:showAsAction="never" /> </menu>
此 XML 文件定义了两个菜单项,分别名为“File”和“Help”。
-
打开“MainActivity.java”,根据图 15 所示添加或修改以下代码。“onCreateOptionsMenu()”方法将膨胀“menu.xml”并将其项目添加到动作栏。“onOptionsItemSelected()”方法将在选择菜单项时被调用,在此练习中,它将简单地在 Toast 消息中回显所选项目的名称。
图 15:处理菜单的代码
任务完成!
干得好,您已经完成了一项探索各种 Android 资源的任务。请观看您劳动的成果,如图 16 至 18 所示。
![]() |
图 16:应用运行中!
|
![]() |
图 17:应用运行中!
|
![]() |
图 18:应用运行中!
|
资源透视
通过实际操作,您已经了解到 Android 将资源组织在项目的“res/”目录中,使用各种子目录按类型和配置对资源进行分组(图 19)。现在是时候整理它们并将其放入透视中了。
![]() |
图 19:res/ 内部
|
Android 一直在悄悄地将所有资源编译成一个名为“R.java”的 Java 类。**R** 代表“Resources”(资源)。每当您的应用编译时,Android 都会生成/重新生成 R 类,其中包含项目中“res/”目录下所有资源的资源 ID。对于每种资源类型,都有一个 R 子类。例如,“R.string”用于所有字符串资源。每个资源都被分配一个唯一的整数作为其资源 ID,以便您可以使用它来检索资源。您可以找到项目中使用的所有资源都声明为 *public static final int 变量*,它们各自的 Java 类也是 *public static final*。由于它们是 R 的子类,因此可以通过调用“**R.<类名>**”来访问,例如“R.string”。
我们将逐一介绍资源目录。
-
**anim** 目录包含定义视图动画的 XML 文件。每个 anim XML 文件定义一个可通过其文件名访问的单个视图动画资源
- 在 XML 中,
@anim/<XML file name>
例如,
@anim/tween_animation
- 在代码中,
R.anim.<XML file name>
例如,
R.anim.tween_animation
- 在 XML 中,
-
**animator** 目录包含定义属性动画的 XML 文件。每个 animator XML 文件定义一个可通过其文件名访问的单个属性动画资源
- 在 XML 中,
@animator/<XML file name>
例如,
@animator/property_animation
- 在代码中,
R.animator.<XML file name>
例如,
R.animator.property_animation
- 在 XML 中,
-
**color** 目录包含定义颜色状态列表资源的 XML 文件。每个 color XML 文件定义一个可通过其文件名访问的单个颜色状态列表资源
- 在 XML 中,
@color/<XML file name>
例如,
@color/button_state
- 在代码中,
R.color.<XML file name>
例如,
R.color.button_state
- 在 XML 中,
-
**drawable** 目录包含可绘制资源,如位图文件和形状。可绘制资源进一步优化为不同尺寸以适应不同的屏幕分辨率。它们存储在独立的目录中,即 *drawable-mdpi*、*drawable-hdpi*、*drawable-xhdpi* 和 *drawable-xxhdpi*。Android 操作系统将根据设备的屏幕分辨率选择其中一张图像进行显示。要了解更多关于 Android 如何支持多屏幕的信息,请访问Android 开发者。每个 drawable XML 文件定义一个可通过其文件名访问的单个可绘制资源
- 在 XML 中,
@drawable/<XML file name>
例如,
@drawable/ic_launcher
- 在代码中,
R.drawable.<XML file name>
例如,
R.drawable.ic_launcher
- 在 XML 中,
-
**layout** 目录包含定义 Android 用户界面视觉结构的 XML 文件。这个主题在我的文章Android UI 布局和控件中已 extensively 讨论。您的项目中只有一个布局文件“activity_main.xml”。
-
**menu** 目录包含所有菜单资源,例如选项菜单、动作栏、上下文菜单和弹出菜单,您可以在您的 Android 项目中使用这些资源。我们的项目中只有一个菜单资源文件“main.xml”。您已经创建了一个动作栏,其中定义了“main.xml”文件中的两个菜单项。每个菜单 XML 文件定义一个可通过其文件名访问的单个菜单资源
- 在 XML 中,
@menu/<XML file name>
例如,
@menu/main
- 在代码中,
R.menu.<XML file name>
例如,
R.menu.main
- 在 XML 中,
-
**raw** 目录包含任何类型的原始文件,这些文件以原始形式保存,不经过 Android 平台编译。在运行时访问这些资源类似于使用“InputStream”访问文件,如下所示
InputStream inputStream = getResources().openRawResource(R.raw.filename);
-
**values** 目录包含 XML 文件,这些文件又包含简单的值资源,例如颜色、尺寸、字符串和样式。与“res/”其他子目录中的 XML 资源文件(每个 XML 文件定义一个资源)不同,“values/”目录中的每个 XML 文件定义多个资源。它的格式如下
<resources> <resourceType name="resourceName">value</resourceType> </resources>
例如,在“strings.xml”中
<resources> <string name="app_name">AndroidResources</string> <string name="hello_world">Hello, Code Project!</string> <string name="click_me">Click Me!</string> <string name="action_file">File</string> <string name="action_help">Help</string> </resources>
-
**color.xml** 定义颜色值
-
**dimens.xml** 定义尺寸资源。Android 推荐使用 dp 和 sp 作为度量单位。
**dp** 是 **density-independent pixels** 的缩写。您也可以使用 **dip**。它们是相同的。它与密度无关,因为当您定义 16dp 或 16dip 的内容时,它将在任何屏幕上以相同的大小渲染。对于 Android 设备繁多的屏幕尺寸来说,这是一个完美的解决方案。它是 Android 应用推荐的尺寸单位。当然,您也可以使用其他度量单位。
Android 还有另一个专门为文本创建的单位。它被称为**比例无关像素**,简称**sp**。它类似于 *dp*,但有一个特点——它允许用户更改字体大小而不影响其他元素的大小。建议您在 Android 中对任何文本使用 **sp**。
-
**strings.xml** 定义字符串资源。
-
**styles.xml** 定义样式和主题。
XML 文件中的每个值资源都可以通过其资源类型和资源名称的组合进行访问
- 在 XML 中,
@<resourceType>/<name of resource>
例如,
@string/app_name
- 在代码中,
R.<resourceType>.<name of resource>
例如,
R.string.app_name
-
-
**xml** 目录包含任意 XML 文件,可在运行时使用“XmlResourceParser”访问,如下所示
XmlResourceParser xml = getResources.getXml(R.xml.filename);
提供替代方案
到目前为止我们讨论的存储在“res/”各种子目录中的资源是所谓的“默认”资源。但由于运行时发生的变化或本地化问题,它们不足以满足 Android 设备的多样化用途和配置。例如,在竖屏方向看起来不错的默认布局在横屏方向可能看起来很糟糕。为此,您将不得不为后者提供**替代**布局,以利用额外的宽度。或者,您的默认字符串资源在当前区域设置中可能运行良好,但可能不适用于其他区域设置。为此,您将不得不提供**替代**字符串资源。底线是“**除了默认资源外,您还应该提供替代资源。**”
Android 框架提供了一种系统化的方法来提供替代资源,并且能够根据运行时检测到的设备配置为您的应用加载适当的资源。让我们通过一个简单的练习来看看它是如何工作的,为您的应用中的不同区域设置(日语)创建替代字符串资源。
-
在“res/”目录下创建一个名为“values-ja”(“ja”是“日语”的**限定符**)的新值资源目录。
-
在“values-ja”目录下,创建一个名为“strings.xml”的新资源文件,并添加以下字符串资源
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello_world">こんにちは, Code Project!</string> <string name="click_me">クリック!</string> <string name="action_file">ファイル</string> <string name="action_help">助け</string> </resources>
-
就是这样!您刚刚为“日语”语言设置创建了替代字符串资源。在 AVD 上试一试,将其语言设置更改为日语,您应该会看到您的应用以日语显示(图 20),或者观看此视频。请注意,应用名称“AndroidResources”保持不变,因为它未在替代资源中定义,因此默认资源优先。您修改了任何代码吗?没有!多么棒的 Android。
图 20:日语字符串资源
那么,您做了什么来创建替代资源呢?让我们回顾一下
- 在“res/”目录下,创建一个新子目录,其命名格式如下
<resourceType>-<qualifier>
例如,
values-ja
-
<resourceType> 是相应默认资源类型(例如 anim、layout、menu、values 等)的目录名称。
-
<qualifier> 是一个名称,表示替代资源应在何种特定设备配置下使用。(详情见表 1)
-
-
在这个“<资源名称>-<限定符>”目录下,创建您的替代资源文件,使用与默认资源目录中对应文件完全相同的名称。例如,“tween_animation.xml”、“main.xml”、“strings.xml”、“dimens.xml”等等。对于值资源文件,例如“colors.xml”、“strings.xml”,您应该为替代值资源文件中包含的每个元素使用完全相同的资源名称。例如,比较默认“values”目录中的默认“strings.xml”与“values-ja”目录中的替代“strings.xml”,如下所示
-
“values/strings.xml”
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">AndroidResources</string> <string name="hello_world">Hello, Code Project!</string> <string name="click_me">Click Me!</string> <string name="action_file">File</string> <string name="action_help">Help</string> </resources>
-
“values-ja/strings.xml”
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello_world">こんにちは, Code Project!</string> <string name="click_me">クリック!</string> <string name="action_file">ファイル</string> <string name="action_help">助け</string> </resources>
-
简而言之,限定符名称是 Android 系统在特定配置下选择哪个替代资源的重要线索。Android 支持许多限定符来表示替代资源,详情请参阅表 1。
类型 | 限定符 | 说明 |
---|---|---|
移动国家码 (MCC) 移动网络码 (MNC) |
示例 **mcc310-mnc070** 属于 AT&T Wireless Inc **mcc525-mnc07** 属于新加坡电信 (Singtel) |
MCC 用于移动网络(GSM、CDMA、UMTS 等),以唯一标识移动用户所属的国家。它通常与 MNC 一起使用,以唯一标识移动用户的网络。 阅读更多信息请访问 mcc-mnc.com。 |
语言和地区 | 示例 en en-rGB en-rSG en-rUS fr fr-rCA fr-rFR |
语言由两位字母的 ISO 639-1 语言代码定义,例如 en 代表英语。 可选地,可以附加一个破折号“-”和一个小写字母“r”,后跟两位字母的 ISO 3166-1-alpha-2 地区代码。例如,en-rSG 代表新加坡的英语。 |
布局方向 | ldltr ldrtl |
**ldltr** 代表“layout-direction-left-to-right”,是任何 Android 应用的默认布局方向。 **ldrtl** 代表“layout-direction-right-to-left”。 这可以应用于任何资源,例如布局、可绘制对象或值。 但是,ldrtl 仅适用于 API 级别 17 或更高的设备。要为您的应用启用 ldrtl,您必须在 AndroidManifest.xml 中将 android:supportsRtl 属性设置为“true”,并将 targetSdkVersion 设置为 17 或更高。 |
最小宽度 | sw<N>dp 示例 sw600dp sw720dp |
这指定了资源应使用的最小宽度(以 <N>dp 为单位),而不考虑屏幕方向。 |
可用宽度 | w<N>dp 示例 w820dp w1024dp |
这指定了资源要使用的最小可用屏幕宽度,以 <N>dp 为单位,当方向在横向和纵向之间切换时,其值将随当前宽度而改变。 |
可用高度 | h<N>dp 示例 h720dp h1024dp |
这指定了资源要使用的最小可用屏幕高度,以 <N>dp 为单位,当方向在横向和纵向之间切换时,其值将随当前高度而改变。 |
屏幕尺寸 | small normal large xlarge |
**small**:大约 320x426 dp 的屏幕。例如 QVGA 低密度和 VGA 高密度。 **normal**:大约 320x470 dp 的屏幕。例如 WQVGA 低密度、HVGA 中密度、WVGA 高密度。 **large**:大约 480x640 dp 的屏幕。例如 VGA 和 WVGA 中密度屏幕。 **xlarge**:大约 720x960 dp 的屏幕。在大多数情况下,它们属于平板设备。 |
屏幕纵横比 | long notlong |
**长**屏包括 WQVGA、WVGA、FWVGA。 **非长**屏包括 QVGA、HVGA 和 VGA。 |
屏幕方向 | port land |
**port** 用于纵向,其中高度大于宽度。 **land** 用于横向,其中高度小于宽度。 当用户旋转设备时,此更改将在运行时发生。 |
UI 模式 |
car watch |
**car** 模式适用于在车载底座中显示的设备。 **desk** 模式适用于在桌面底座中显示的设备。 **television** 模式适用于在电视上显示的设备。 **watch** 模式适用于具有显示屏并戴在手腕上的设备。 **appliance** 模式适用于用作电器且无显示单元的设备。 |
夜间模式 | night notnight |
**night** 用于夜间。 **notnight** 用于白天。 |
屏幕密度 |
ldpi |
**ldpi** 用于大约 120dpi 的低密度屏幕。 **mdpi** 用于大约 160dpi 的中密度屏幕。 **hdpi** 用于大约 240dpi 的高密度屏幕。 **xhdpi** 用于大约 320dpi 的超高密度屏幕。 **xxhdpi** 用于大约 480dpi 的更高密度屏幕。 **nodpi** 用于密度无关资源,您不希望它们无论当前屏幕密度如何都被缩放。 **tvdpi** 用于大约 213dpi 的屏幕密度。它主要用于电视。 |
触摸屏可用性 | finger notouch |
**finger** 表示设备具有触摸屏。 **notouch** 表示设备没有触摸屏。 |
键盘可用性 |
keysexposed |
**keysexposed** 表示设备有硬件键盘。 **keyshidden** 表示设备有硬件键盘但它被隐藏,并且没有启用软键盘。 **keyssoft** 表示设备启用了软键盘。 |
主要文本输入法 |
nokeys |
**nokeys** 表示设备没有用于文本输入的硬件按键。 **qwerty** 表示设备有硬件全键盘。 12key 表示设备有硬件 12 键键盘。 |
导航键可用性 |
navexposed navhidden |
**navexposed** 表示导航键可供用户使用。 **navhidden** 表示导航键不可用。 |
主要非触控导航方法 |
nonav |
**nonav** 表示设备除触摸屏外没有其他导航功能。 **dpad** 表示设备有方向键 (d-pad) 用于导航。 **trackball** 表示设备有轨迹球用于导航。 **wheel** 表示设备有方向滚轮用于导航。 |
API 级别 |
示例 |
这表示设备支持的 API 级别。例如,v1 代表 API 级别 1,v4 代表 API 级别 4。有关这些值的更多信息,请参阅Android API 级别文档。 |
遵守规则
提供替代资源时需要遵守一些规则
-
您不能在一个资源目录中嵌套另一个资源目录。例如,“**res/values/values-ja/**”是非法的。
-
您可以通过用破折号“-”分隔每个限定符,向单个资源目录名称添加多种不同类型的限定符。例如,“**values-en-rSG-port**”适用于纵向的新加坡英语设备。
-
但是,您必须按照表 1 中列出的顺序将它们添加到目录名称中。以上一个示例为例,“**values-port-en-rSG**”是不可接受的。
-
您不能向单个资源目录名称添加多个相同类型的限定符。例如,“**layout-land-port**”毫无意义。
最佳实践
随着我们接近休息站,我总结了一些您应该遵循的最佳实践,以便正确高效地管理您的 Android 应用中的资源。
关注点分离
我必须再次强调这一点,原因我在本文开头已经提到过。将资源与代码分离。永远不要硬编码字符串值或数字,将其分配给适当的资源类型,然后您就可以在需要时引用它。
提供默认资源
您不必为您应用使用的每种资源类型都提供替代资源,但您绝对必须提供一套完整的默认资源。为什么?想象一下,您将所有资源存储在带有限定符的目录中。当您的应用试图在没有指定任何限定符配置的设备上运行时,它将崩溃!如果您在默认资源目录中提供了资源(即没有任何限定符的目录),您的应用将切换到使用这些默认资源并继续正常运行。
在您之前完成的本地化练习中,您为日语提供了一套替代字符串资源,同时提供了英语的默认字符串资源。日语版本并没有默认版本拥有的所有字符串资源。(我故意省略了“app_name”。)但这没关系,因为缺失的替代资源将由相应的默认资源来弥补,只要它存在。这就是为什么您的应用使用的所有资源类型都必须提供默认资源。
提供默认资源的另一个原因是,您的应用可能在旧版本的 Android 设备上运行,这些设备无法识别最新版本中引入的一些新限定符。同样,如果没有默认资源可供回退,这也会导致应用崩溃。
因此,提供资源的正确方法是始终为您的应用正常运行所需的所有资源类型提供默认资源。然后使用适当的限定符为特定设备配置创建替代资源。
您可能会注意到我们的项目中有一个矛盾之处。没有默认的“drawable”目录!嗯,这是一个例外,正如 Android 开发者所解释的
引用这条规则有一个例外:如果您的应用程序的 minSdkVersion 为 4 或更高,则当您提供带有屏幕密度限定符的替代 drawable 资源时,您*不需要*默认的 drawable 资源。即使没有默认的 drawable 资源,Android 也能在替代屏幕密度中找到最佳匹配并根据需要缩放位图。但是,为了在所有类型的设备上获得最佳体验,您应该为所有三种密度类型提供替代 drawable。
避免过度提供替代方案
尽管您必须提供应用所需的全套资源,但您的替代资源不必拥有默认资源的所有内容。例如,当您计划特定于区域设置的替代方案时,几乎不需要替代布局。但是,您可能需要替代布局来应对运行时屏幕方向的变化。仔细研究需求将有助于您确定应用真正需要哪些替代资源。这将有助于减少开发时间和成本,并随后简化维护。
测试默认资源
您应该测试您的应用所有默认资源类型,无论是语言、方向、屏幕尺寸等等。例如,通过将测试设备设置为您的应用不支持的语言来测试您的应用。如果应用崩溃,则表明默认字符串资源目录中缺少字符串。对其他资源类型也进行相同的测试。
技巧
我在这里放置了一些与资源相关的提示,以帮助您开发 Android 应用。
提供不同的位图 Drawable 以匹配不同的屏幕密度
默认情况下,Android 会缩放您的位图 drawable,以便它们可以在不同屏幕尺寸上正确渲染。然而,这种自动缩放的结果并非最佳。相反,建议您提供针对不同屏幕密度的不同分辨率的 drawable。
为不同屏幕尺寸提供不同的布局
默认情况下,Android 会调整您的应用布局以适应不同的设备屏幕。然而,强行将小号衬衫套在大身材上,反之亦然,绝非好事。相反,您应该为不同屏幕尺寸提供不同的布局。
显式声明屏幕尺寸
您应该通过在应用的清单文件中包含 <supports-screens> 元素来明确声明您的应用支持的屏幕尺寸。通过这种方式,您可以确保只有具有匹配屏幕尺寸的设备才能下载您的应用。
在休息站
我们已经到达了休息站。这是一次充满资源的旅程。这次旅程始于您进行一项动手练习,探索、创建并为 Android 应用提供不同类型的资源。除此之外,您还学习了 Android 如何组织资源,提供默认和替代资源的最佳实践,以及访问资源的不同方式。我希望从现在开始,您在管理 Android 资源方面变得更加得心应手。