转自 http://blog.163.com/wangli_601/blog/static/122950173201162484451943/
在第二章,介绍了Android应用程序以及对于一些底层的思路作了一个简短的介绍。同时也学习了Android SDk,ADT,以及如何在模拟器上运行Android应用程序。
在接下来的章节中,我们将带领大家对Android SDK的功能进行深入的学习。内容包括:资源访问(resource),内容提供者(content provider)和意图(intent)。深入理解这三个知识点能够帮助我们更加好的学习后续的更加深入的内容。
Android使用资源的方式来定义界面组件。这种方式和使用HTML标签去定义界面很相似。由此可见,Android定义界面的方式是非常有远见的。这种方式使Android很好支持本地化。在本章,我们将介绍如何使用Android中的各种资源。
理解资源
资源在Android中扮演着非常重要的角色。在Android中资源可以是一个文件(例如音乐)也可以是一个值(例如一个对话框的标题信息),这些资源被绑定到应用程序中。我们可以在不需要重新编译应用程序的情况下改变应用程序中的这些资源。
常见的资源包括字符串(string),颜色(color)和位图(bitmap)。你可以使用这些资源(比如说字符串)的编号去访问这些资源,而不是在程序中进行硬编码了。这样的方式使你在改变你的字符串的时候,不需要改变你的源码。
在Android中包含了许许多多的资源类型。我们将在本章介绍大量的资源,让我们从最常用的资源——字符串(string)开始学习。
字符串资源
Android可以在一个或者多个XML资源文件中定义字符串。这些XML文件存放在/res/values文件夹下,在string-resource中定义字符串。对于这些文件的文件名没有任何限制,虽然在普通的情况下我们看到这个文件叫strings.xml。下图是一个字符串资源文件的实例:
当这个文件被创建或者更新的时候,ADT会自动的同步创建或者更新应用程序中的一个存放在应用程序根目录叫做R.java的文件。在R.java中使用两个唯一的编号去表示这两个字符串资源。
请注意下面的R.java文件,我们给出了一个叫做MyProject工程的目录结构
对应上面的工程,R.java的实例如下
package com.mycompany.android.my-root-package;
public final class R {
...other entries depending on your project and application
public static final class string
{
...other entries depending on your project and application
public static final int hello=0x7f040000;
public static final int app_name=0x7f040001;
...other entries depending on your project and application
}
...other entries depending on your project and application
}
首先值得注意的是R.java最外层类的定义为public static final class R。出此之外,Android还定义了一个内部类,名称为static final class string。R.java创建这个内部的静态类去管理字符串的编号。
两个整型的静态变量hello和app_name的编号代表了两个字符串资源。你可以在代码中像R.string.hello这样使用这些资源编号。
生成的整型编号要比字符串使用的更为广泛,大多数方法在需要一个字符串作为参数的时候都会有一个传入整型编号作为参数的重载方法。Android在任何需要的地方都会把这些整型的编号解析成它所引用的字符串。
一般情况下,在大多数简单的应用程序中把所有的字符串资源都应以在一个名叫strings.xml文件中。其实在Android中可以再/res/values下定义很多的任意文件名的XML文件定义字符串,并且文件结构如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello1">hello 1</string>
<string name="app_name1">hello appname 1</string>
</resources>
Eclipse ADT插件可以在编译代码的时候检查在R.java中生成的编号的唯一性。
布局资源
在Android中,屏幕所显示的视图通常都是在XML文件中定义的。这些定义视图的XML文件我们叫它布局资源文件。布局文件在Android界面编程中是一个非常重要的资源文件。仔细的查看已下activity的代码。
public class HelloWorldActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
}
…
}
通过setContentView(R.layout.main)这行代码,我们可以看出有一个静态的类叫做R.layout,并且在这个类中,定义了一个名为main的整型变量来引用一个XML布局资源文件。
这个名叫main.xml的布局资源文件,存放在layout文件夹下。换句话说,这条语句期望程序员在/res/layout/文件夹下定义一个main.xml的布局资源文件,并且在这个文件中定义需要的布局样式。main.xml文件的内容如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button android:id="@+id/b1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
在这个布局文件中有一个叫LinearLayout的根节点,在这个节点中包含了一个TextView和一个Button节点。LinearLayout使它的子节点作横向的(horizontally)或者纵向的(vertically)排列,在这个事例中两个控件是纵向(vertically)排列的。
举个例子,如果在/res/layout/下面定义了file1.xml和file2.xml两个布局资源文件,那么将在R.java中生成如下代码:
public static final class layout {
.... any other files
public static final int file1=0x7f030000;
public static final int file2=0x7f030001;
}
在这些布局文件中定义的控件,例如TextView可以通过R.java中生成的ID进行访问。
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
在该事例中,在Activity类里通过findViewById这个方法获得了TextView对象的引用。
R.id.text1代表了TextView定义的ID。这个ID通过如下方式定义:
<TextView android:id="@+id/text1"
..
</TextView>
id这个属性的值是text1,这个值是该控件的唯一标识。@+id/text1中前面的+号意思是如果不存在这个ID就创建改ID。关于更多的ID的语法,我们将在接下来探讨。
资源引用语法
Android中所有的资源通过它们的id在Java文件中被引用。在XML中为资源分配id的语法称之为资源引用语法。id属性的值例如前一个实例中的@+id/text1遵循如下正式的结构:
@[package:]type/name
type需要符合在R.java中的这些资源类型命名空间的其中一个,这些资源类型命名空间有:
R.drawable
R.id
R.layout
R.string
R.attr
R.plural
R.array
而在XML文件中的语法如下:
drawable
id
layout
string
attr
plurals
string-array
name部分是给定的资源的名称。这个名称在R.java中也是整型常量的名称。
如果你没有具体指定任何包:@[package:]type/name,则package默认为本地资源:R.java所在的包。
如果指定android:type/name,将会引用android包中的ID,这个ID是通过android.R.java文件指定的。你可以使用任何包名替代[package:],以此找到关联的R.java文件。基于以上信息,来分析一个例子。根据下列代码,注意”android:id”不是语法部分,”android:id”是分配ID的属性。
<TextView android:id="text">
//编译错误,text不是一个原始字符串的ID
<TextView android:id="@text">
//错误的语法,@text没有指定type部分
//应该改成@id/text,@+id/text或者@string/string1
//这个错误将提示 No Resource type specified
<TextView android:id="@id/text">
//除非你已经定义了一个名称叫做text的编号
//否则将出现No Resource found that matches id "text"的错误提示
<TextView android:id="@android:id/text">
//错误:资源不是Android公共的
//在android.R.id中没有定义这个id
//当然如果Android R.java中定义了这个名为text的id,以上就是合法的
<TextView android:id="@+id/text">
//成功:在当前包中创建了一个名为text的id
在”@+id/text”这样的语法中,”+”号有着特殊的含义。它告诉Android这个ID可能不存在,如果是这样的话,就创建一个名为text的新的ID。
预先定义自己的资源编号
对于分配一个ID的一般模式是要么创建一个新的(@+id/test)或者使用一个在Android包中已经定义的(@android:id/list)。但是,还可以在预先定义一些ID然后再你自己的包中使用它们。
<TextView android:id=”@+id/text”>前面这段代码表示如果这个控件的id名称已经存在就可以直接使用,如果不存在,就会创建一个新的。因此什么时候会在R.java中存在像text这样能被反复使用的id值?
你可能比较倾向于在R.java中定义R.id.text这样的常量,但是R.java是不可编辑的。尽管如此,当对/res/*下面的子文件夹中的资源文件进行修改,添加或者删除的操作的同时,R.java都会随之更改。
这样,解决的办法就是定义一个资源文件,使用item资源标签定义id。请看一下代码
<resources>
<item type=”id” name=”text”/>
</resources>
在上述代码中type属性表示@[package:]type/name中的type--id。一旦定义好了ID那么就可以像如下代码一样给控件分配ID
<TextView android:id=”@id/text”>
..
</TextView>
编译与反编译Android资源
Android主要支持两种类型的文件资源:XML文件和原始文件(包括图片,音频和视频等)。在XML文件中定义的值资源,以及把整个XML文件作为一个资源都是可见的。
进一步分析XML资源文件,你会发现两种类型:一类是被编译成二进制格式,另一类为是原样复制到设备。根据你所见过的例子,字符串XML资源文件和布局资源文件,在编译成二进制格式之后打包成为安装包。这些预定义的XML文件格式XML节点转换为ID。
你也可以选择一些有自身的结构的XML文件,这些文件Android将不会对其经行转换成ID。但是,你希望他们编译成二进制格式并且拥有本地化。要做到这一点,你可以把这些XML文件放置在/res/xml/子目录下,它们会被编译成二进制格式。在这种情况下,你需要使用Android提供的XML读取器读取XML节点。
如果你将文件(包括XML文件)存放在/res/raw/目录下,那么这些文件将不会编译成二进制文件。你必须使用特定的流操作来读取这些文件。音频与视频文件包含在此类中。
正如我们前面章节提到的,资源文件根据它们各自的类型存放在不同的子目录中。在
/res下有一些重要的子目录如下:
anim:编译后的动画文件
drawable:位图文件
layout:布局文件
values:数组,颜色,尺寸,字符串以及样式文件
xml:编译后的XML文件
raw:原生的文件
资源文件编译器Android Asset Packaging Tool(AAPT)编译所有的资源文件,除了原生资源以及最终的.apk文件。.apk文件包含Android应用程序的代码和资源,类似于Java的.jar
文件。.apk文件是Android设备的安装文件。
列举重要的Android资源
现在我们已经了解了一些基本的资源,接下来我们将列举一些另外的Android支持的重要资源:它们的XML的含义和如何在Java代码中使用这些资源。(你可以把这张作为使用各种资源文件的快速入门)。为了开始这部分内容,下面列举了各种资源类型的用途:
资源类型 | 存放目录 | 描述 |
颜色(Color) | /res/values/any-file | 颜色编号。这些资源编号在R.java中表示为R.color.*。在XML文件中结构为 /resources/color |
字符串(String) | /res/values/any-file | 字符串资源。字符串资源允许加入Java格式化后的字符串和原生的HTML。这些资源编号在R.java中表示为R.string.*。在XML文件中结构为/resources/string |
字符串数组(String arrays) | /res/values/any-file | 代表字符串数组资源。这些资源编号在R.java中表示为R.array.*。在XML文件中结构为 /resources/string-array |
复数(Plural) | /res/values/any-file | 表示一个基于总量的一个合适的字符串集合。总量是一个数值。各种语言,你写一个语句的方式取决于是否存在对对象的引用。这些资源编号在R.java中表示为R.plural.*。在XML文件中结构为/resources/plurals |
动画(Dimensions) | /res/values/any-file | 在Android中代表动画或者各种元素的大小或者视图资源。支持像素,英寸,毫米,与屏幕密度无关的像素(dip)和与刻度无关的像素(sip)。这些资源编号在R.java中表示为R.dimen.*。在XML文件中结构为/resources/dimen |
图片(Image) | /res/drawable/multiple-files | 代表图片资源。支持的图片类型包括:.jpg,.gif,.png,等等。每一个图片都是一个独立的文件并且通过这些图片的ID获得。这些图片ID名称有图片的文件名决定。这些资源编号在R.java中表示为R.drawable.*。图像还支持一种被称为可伸展的图像,是图像的部分伸展而其他部分保留静态图像。这种可伸展图像也被称为9-patch文件。 |
颜色位图(Color drawable) | /res/values/any-file /res/values/multiple-files | 代表颜色为视图中使用的矩形背景位图或者一般得位图。这种资源可以用来指定带个色彩作为背景。在Java中,这相当于创建一个彩色的矩形作为视图背景。这些资源在R.java中表示为R.drawable.*。在XML文件结构中为/res/drawable Android通过在XML文件中定义<shape>资源也支持圆角矩形和梯形的图像。这些资源ID在R.java中表示为R.drawable.*。每一个文件名作为唯一的ID标识。 |
任意的XML文件 (Arbitary XML files) | /res/xml/*.xml | Android允许任意的XML文件作为资源。这些文件将会被AAPT编译器编译。这些资源ID在R.java中表示为R.xml.*。 |
任意原生资源 (Arbitrary raw resources) | /res/raw.*.* | Android允许任意的不被编译的二进制文件或者文本文件存在在这个目录下。每个文件都有一个唯一的资源ID。这些资源ID在R.java中表示为R.raw.*。 |
任意固定资源 (Arbitrary raw assets) | /assets/*.*/*.* | Android允许在/asset目录下创建任意的文件夹下存放任意的文件。这些不是真正的资源,只是原生文件。这个目录,不像/res资源子目录,允许任意深度的子文件夹。这些文件不生成资源ID。你必须使用不包含/assets的相对路径访问这些文件。 |
在上表中介绍的各种资源,将在接下来的章节中详细的介绍如何在XML文件和Java代码中使用这些资源。
字符串数组
你可以在/res/values子目录下指定一个字符串数组的资源文件。这时候你需要使用string-array元素。这个元素与字符串元素一样是resources的子元素。下面是一个字符串数组的资源文件的实例
<resources ….>
……Other resources
<string-array name="test_array">
<item>one</item>
<item>two</item>
<item>three</item>
</string-array>
……Other resources
</resources>
一旦你定义了这个字符串数组,你就可以像如下Java代码一样使用这个资源:
Resources res = your-activity.getResources();
String strings[] = res.getStringArray(R.array.test_array);
//Print strings
for (String s: strings)
{
Log.d("example", s);
}
复数
复数是一个字符串的集合。这些字符串用各种方式表示一个数字总量,例如,在篮子里有多少个鸡蛋。有如下例子:
There is 1 egg.
There are 2 eggs.
There are 0 eggs.
There are 100 eggs
注意这些句子2,0和100相同,然而1是不同的。Android允许你使用复数资源表示这种变化。以下例子表示了在一个资源文件中基于数量的两种变化。
<resources…>
<plurals name="eggs_in_a_nest_text">
<item quantity="one">There is 1 egg</item>
<item quantity="other">There are %d eggs</item>
</plurals>
</resources>
请注意这两个变化是一个多元的两个不同的字符串表示。现在,你可以使用Java代码,如下列例子所示,使用这个复数给定一个数量打印字符串。getQuantityString()方法的第一个参数代表复数的资源的ID,第二个参数选择要使用的字符串。当值是1,我们只需要使用原样的字符串。当值不等于1,我们必须提供一个。第三个参数的值将替换%d。如果你要使用复数资源的格式化字符串,你至少需要这三个参数。
Resources res = your-activity.getResources();
String s1 = res.getQuantityString(R.plurals.eggs_in_a_nest_text, 0,0);
String s2 = res.getQuantityString(R.plurals.eggs_in_a_nest_text, 1,1);
String s3 = res.getQuantityString(R.plurals.eggs_in_a_nest_text, 2,2);
String s4 = res.getQuantityString(R.plurals.eggs_in_a_nest_text, 10,10);
在给出的这段代码中,每个数量将会找到合适的复数匹配在在结果字符串。
然而,quantity属性节点存在其它的可能性呢?我们强烈建议您阅读Resources.java和PluralRules.java源代码。在本章最后部分的资源连接到这些源码文件。
最后一行是这样的,在基于english的环境中,只有两种可能性“one”和“other”。其他语言也是如此,除了捷克,在这种情况下值“one”是1,“few”是2到4,以及“other”为剩下的部分。
更多关于字符串资源的话题
在之前的内容中我们简要讨论字符串资源的相关知识。让我们重新回顾一下,并且增加一些微妙的知识,包括HTML字符串和如何在字符串资源中使用占位符。
我们将从分析一个包含定义普通字符串,引用字符串,HTML字符串和包含占位符的字符串的示例XML资源文件开始:
<resources>
<string name="simple_string">simple string</string>
<string name="quoted_string">"quoted 'xyz’ string"</string>
<string name="double_quoted_string">\"double quotes\"</string>
<string name="java_format_string">
hello %2$s Java format string. %1$s again
</string>
<string name="tagged_string">
Hello <b><i>Slanted Android</i></b>, You are bold.
</string>
</resources>
这个字符串的XML资源文件需要放在/res/values子目录下。文件的名字可以任意。
请注意引号的字符串必须要么去掉引号,或者使用转义的引号。该字符串的定义也允许标准Java字符串格式化序列。
Android允许在<string>节点中加入XML子元素,例如<b>,<i>,以及其它简单的文本格式化的HTML标签。你可以在打印文本之前使用HTML标签对文本进行样式处理。
使用这些字符串的Java代码如下:
//读取一个简单的字符串并且给textView设置文本
String simpleString = activity.getString(R.string.simple_string);
textView.setText(simpleString);
//读取带引号的字符串并且给textView设置文本
String quotedString = activity.getString(R.string.quoted_string);
textView.setText(quotedString);
//读取双重转义字符串并且给textView设置文本
//Read a double quoted string and set it in a text view
String doubleQuotedString = activity.getString(R.string.double_quoted_string);
textView.setText(doubleQuotedString);
//读取Java格式化字符串
String javaFormatString = activity.getString(R.string.java_format_string);
//根据传入的参数格式化字符串
String substitutedString = String.format(javaFormatString, "Hello" , "Android");
//设置textView文本
textView.setText(substitutedString);
//读取HTML字符串
String htmlTaggedString = activity.getString(R.string.tagged_string);
//转换字符串并且给textView设置文本
//android.text.Html类允许打印HTML字符串
//这个类是Android指定处理HTML标签的类,但是不支持所有的HTML标签
Spanned textSpan = android.text.Html.fromHtml(htmlTaggedString);
//设置textVIew的文本
textView.setText(textSpan);
一旦你已经定义了字符串资源,你就可以直接在XML布局定义中使用,例如以下给TextView设置文本:
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/tagged_string"/>
TextView会自动识别这个字符串是一个HTML字符串,并且做相应的格式化处理。这样做是很好的,因为你能快速的在你的视图组件部分设置文本属性。
颜色资源
当你能够使用字符串资源,你就能够使用引用标识符去直接引用颜色。这样做可以使Android使用颜色以及应用主题。一旦你在资源文件中定义了颜色标识,你就能通过它们的ID在Java代码中访问他们。与字符串资源在<your-package>.R.string命名空间类似,可用的颜色ID也在<your-package>.R.color命名空间中。
Android在自己的资源文件中定义了一些基本的颜色。这些ID通过android.R.color命名空间访问。通过这个地址去了解android.R.color中定义的颜色常量:
http://code.google.com/android/reference/android/R.color.html
请看一下颜色XML资源文件的示例:
<resources>
<color name="red">#f00</color>
<color name="blue">#0000ff</color>
<color name="green">#f0f0</color>
<color name="main_back_ground_color">#ffffff00</color>
</resources>
以上的颜色资源文件需要放在/res/values子目录下。文件名称是任意的,意味着你可以选择任何的文件名。Android将会读取所有的文件然后执行他们并且查找特别的节点比如说资源文件和颜色计算出特别的编号。
下面示例显示了如何在Java代码中使用颜色资源
int mainBackGroundColor = activity.getResources.getColor(R.color.main_back_ground_color);
下面示例如何在定义控件的时候使用颜色资源
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@color/red"
android:text="Sample Text to Show Red Color"/>
尺寸资源
像素,英寸和点是所有能够和XML布局和Java代码中使用的尺寸。你可以使用这些尺寸在不改变源代码的情况下给Android界面添加样式。
下面示例展示了如何在XML文件中使用尺寸资源
<resources>
<dimen name="mysize_in_pixels">1px</dimen>
<dimen name="mysize_in_dp">5dp</dimen>
<dimen name="medium_size">100sp</dimen>
</resources>
你可以使用一下单位指定尺寸:
px:像素
in:英寸
mm:毫米
pt:点
dp:以160-dpi为基础的与屏幕像素密度无关的单位
sp:与刻度无关的像素(作用于字体)
在Java中,你需要通过Resources对象的实例访问相关的尺寸。你可以调activity
对象的getResources()方法获得Resources对象。当你获得了Resources对象后,你就可以通过尺寸ID获得尺寸数据。例如:
float dimen = activity.getResources().getDimension(R.dimen.mysize_in_pixels);
与在Java代码中访问尺寸资源相同,使用dimen代替dimension。例如:
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/medium_size"/>
图片资源
Android通过存在在/res/drawable/子目录下的图片文件生成图片资源ID。支持的图片类型包括.gif,.jpg和.png。根据在这个目录下的每一个图片文件的文件名生成一个唯一的ID。假如有一个图片名称为sample_image.jpg的图片,那么将会生成的ID为:
R.drawable.sample_image。
值得注意的是,在/res/drawable中不能有两个同名的文件,并且该目录不能包含子目录,如果任何文件放在该目录的子目录下,那么Android将不可见。
下面示例代码表示了再XML布局中使用图片资源:
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Dial"
android:background="@drawable/sample_image"
/>
你同样可以使用Java代码为UI组件设置颜色,例如Button:
//调用getDrawable()方法获得图片
BitmapDrawable d = activity.getResources().getDrawable(R.drawable.sample_image);
//使用图片设置背景
button.setBackgroundDrawable(d);
//或者通过资源ID设置背景
button.setBackgroundResource(R.drawable.sample_image);
Android也支持一种叫做可伸展图片的特殊图片类型。这是一种是部分伸展而其他部分保留静态的.png图像。Android提供一个叫做Draw 9-patch的工具来伸展这些区域(你可以在http://developer.android.com/guide/developing/tools/draw9patch.html阅读到更多信息。)
当这种.png图片设置成可用,你就可以像使用其他图片一样的使用了。它在给按钮设置背景的时候非常的便利,特别是当不得不拉伸按钮来适应文件尺寸的时候。
颜色图片资源
在Android里,图片是一种可绘制的资源。然而Android还支持另一种可绘制的资源,这种资源叫做颜色图片资源(color-drawable)。这种资源实际上就是一个着色了的矩形。
为了定义一个有色的矩形,你需要在/res/values子目录中的任何XML文件中定义drawable元素。例如:
<resources>
<drawable name="red_rectangle">#f00</drawable>
<drawable name="blue_rectangle">#0000ff</drawable>
<drawable name="green_rectangle">#f0f0</drawable>
</resources>
在Java代码中使用颜色图片资源
// 获得一个颜色图片
ColorDrawable redDrawable = (ColorDrawable)
activity.getResources().getDrawable(R.drawable.red_rectangle);
//为textView设置背景
textView.setBackgroundDrawable(redDrawable);
在XML中使用:
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAlign="center"
android:background="@drawable/red_rectangle"/>
为了实现圆角的图像,你可以使用在文档中没有<shape>标签。但是,这个标签需要在/res/drawable目录下作为一个独立的文件。下面的示例展示了如何在/res/drawable/my_roun-
ded-rectangle.xml文件中使用<shaper>标签定义一个圆角图形。
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#f0600000"/>
<stroke android:width="3dp" color="#ffff8080"/>
<corners android:radius="13dp" />
<padding android:left="10dp" android:top="10dp"
android:right="10dp" android:bottom="10dp" />
</shape>
接下来,你就可以使用这个图片资源作为前面TextView的背景了。例如:
//获得图片
GradientDrawable roundedRectangle =
(GradientDrawable)
activity.getResources().getDrawable(R.drawable.my_rounded_rectangle);
//为textView设置背景
textView.setBackgroundDrawable(roundedRectangle);
与任意的XML资源文件一起工作
为了使用结构化的资源,Android允许任意的XML文件作为资源。这是使用任意XML文件作为资源使用的高级方式。首先,它提供了一种快速的方式通过这些文件生成的资源ID去引用这些资源文件。其次,这种方式允许你本地化这些XML资源文件。第三,你可以再设备上高效的编译和存储这些XML文件。
需要进行读取操作的XML文件需要存储在/res/xml目录下。以下是一个叫做/res/xml/test.xml文件的示例:
<rootelem1>
<subelem1>
Hello World from an xml sub element
</subelem1>
</rootelem1>
与其它的XML资源文件一样,AAPT会在打包应用程序之前编译这个XML文件。如果你要解析这些文,件你需要使用XmlPullParser类型的实例。你可以如下所示,在任何上下文中使用XmlPullParser解析XML文件。
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
返回的XmlResourceParser是XmlPullParser的实例,并且它实现了java.util.AttributeSet接口。下面示例代码将读取test.xml文件:
private String getEventsFromAnXMLFile(Activity activity) throws XmlPullParserException, IOException
{
StringBuffer sb = new StringBuffer();
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT)
{
if(eventType == XmlPullParser.START_DOCUMENT)
{
sb.append("******Start document");
}
else if(eventType == XmlPullParser.START_TAG)
{
sb.append("\nStart tag "+xpp.getName());
}
else if(eventType == XmlPullParser.END_TAG)
{
sb.append("\nEnd tag "+xpp.getName());
}
else if(eventType == XmlPullParser.TEXT)
{
sb.append("\nText "+xpp.getText());
}
eventType = xpp.next();
}//eof-while
sb.append("\n******End document");
return sb.toString();
}//eof-function
在以上代码中,你可以看到如何得到XmlPullParser对象,如何使用XmlPullParser对象操作XML文档的元素,以及如何使用XmlPullParser的方法访问XML元素的详细内容。如果你想运行这段代码,你需要首先创建一个XML文件,然后调用getEventsFromAnXMLFile方法。这个方法将返回一个字符串,你可以用Log.d调试方法输出。
使用原生资源
除了任意的XML文件,Android还支持原生文件。这些原生文件例如音频,视频以及本地化的或者需要引用资源ID文本文件都存放在/res/raw目录下,而不像XML文件存放在/res/xml目录下。这些原生文件直接打包到应用程序而不需要进行编译。尽管如此,每个原生文件都会在R.java中生成一个标识。如果你存放一个文本文件在/res/raw/test.txt目录,你需要使用下列代码读取该文件:
String getStringFromRawFile(Activity activity)
throws IOException
{
Resources r = activity.getResources();
InputStream is = r.openRawResource(R.raw.test);
String myText = convertStreamToString(is);
is.close();
return myText;
}
String convertStreamToString(InputStream is)
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = is.read();
while (i != -1)
{
baos.write(i);
i = is.read();
}
return baos.toString();
}
使用固定资源
Android还提供了一个目录/assets,你可以再这个目录下存放文件。这个目录与/res是同级目录,也就是说该目录不是/res的子目录。存放在/assets中的文件不会在R.java中生成资源ID,你必须指定路径去读取这些文件。相对路径从/assets开始。可以使用AssetManagerl类访问这些文件,示例代码如下:
String getStringFromAssetFile(Activity activity)
{
AssetManager am = activity.getAssets();
CHAPTER 3: Understanding Android Resources 83
InputStream is = am.open("test.txt");
String s = convertStreamToString(is);
is.close();
return s;
}
浏览资源目录结构
总体来说,以下提供了一个整体资源目录机构
/res/values/strings.xml
/colors.xml
/dimens.xml
/attrs.xml
/styles.xml
/drawable/*.png
/*.jpg
/*.gif
/*.9.png
/anim/*.xml
/layout/*.xml
/raw/*.*
/xml/*.xml
/assets/*.*/*.*
资源和配置的变化
资源可以帮助本地化的实现。例如,你可以根据用户的语言环境改变一个字符串的值。在设备的任何配置上Android都推行这种思想而语言配置只是配置中的一项。另一个配置改变的例子是当设备从垂直模式转向水平模式的时候。垂直模式被称作是水平模式的景观模式。
Android允许在基于相同的布局资源ID的情况下,选择不同的布局。Android使用不同的目录对应每个配置来实现。实例如下:
\res\layout\main_layout.xml
\res\layout-port\main_layout.xml
\res\layout-land\main_layout.xml
尽管在三个不同的目录下都存在一个名为main_layout.xml的布局文件,但是在R.java中只会生成一个资源ID:R.layout.main_layout。但是,当你检索到这个布局ID,你将会获得一个适合你设备布局的布局资源。
在这个例子中,扩展的目录-port和-land被称为已配置限定符(configuration qualifiers),这些配置限定符不区分大小写,并且使用连接符(-)分隔资源目录。这些配置中指定的包含限定符的目录被称为替代资源。在资源目录的资源与这些配置限定符被称为默认资源。
可用的配置限定符如下表:
mccAAA:AAA是移动国家代码
mncAAA:AAA是运营商/网络代码
en-rUS:语言和地区
small,normal,large,xlarge:屏幕尺寸
long,notlong:屏幕类别
port,land:纵向或横向
car,desk:类型对接
night,notnight:黑夜或白天
ldpi,mdpi,hdpi,xhdpi,nodpi:屏幕密度
notouch,stylus,finger:什么类型的屏幕
keysexposed,keyssoft,keyshidden:什么类型的键盘
nokeys,qwerty,12key:多少个按键
navexposed,navhidden:隐藏或显示导航键
nonav,dpad,trackball,wheel:导航驱动类型
v3,v4,v7:API级别
使用这些资源符,你可以诸如如下的资源目录:
\res\layout-mcc312-mnc222-en-rUS
\res\layout-ldpi
\res\layout-hdpi
\res\layout-car
通过可用设备上的导航用户本地应用程序你可以发现当前场所。导航的路径是Home-List of Applications-Coston Locale。
提供一个资源ID,Android使用一个算法获得正确的资源。你可以在Reference URLs章节中学习到关于RULs更多的规则,但是我们将指出一些使用规则的经验。
主要的规则是这些配置限定符的优先级,思考如下列表:
\res\layout\main_layout.xml
\res\layout-port\main_layout.xml
\res\layout-en\main_layout.xml
在以上列表中,布局文件main_layout.xml有两种额外的变化。有一个用于布局模式,另一个用于语言。现在,让我们研究当我们看到的视图为纵向模式时,Android是如何选择布局文件的。
尽管你处于纵向模式下,Android将闲哦那个layout-en目录下选择布局文件,因为在配置限定符中语言的变化优先于方向的变化。该SDK链接,在“参考网址”中明确列出所有的配置限定符的优先级。
让我们用优先级的规则与一些字符串资源做进一步的试验。请记住,这些字符串资源都有各自的ID,然而布局资源是基于文件的。为了使用字符串对配置限定符的优先级进行测试,我们拿出五个资源ID,进行如下变化:default,en,en_us,port和en_port。五个资源ID如下:
teststring_all:这个ID将参与所有的变化
testport_port:这个ID将出现在default以及-port变化中
t1_enport:这个ID将出现在default,-en和-port中
t1_1_en_port:这个将出现在default和-en-port中
t2:这个只出现在default中
代码如下:
// values/strings.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">teststring in root</string>
<string name="testport_port">testport-port</string>
<string name="t1_enport">t1 in root</string>
<string name="t1_1_en_port">t1_1 in root</string>
<string name="t2">t2 in root</string>
</resources>
// values-en/strings_en.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">teststring-en</string>
<string name="t1_enport">t1_en</string>
<string name="t1_1_en_port">t1_1_en</string>
</resources>
// values-en-rUS/strings_en_us.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
ame="teststring_all">test-en-us</string>
</resources>
// values-port/strings_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-us-port</string>
<string name="testport_port">testport-port</string>
<string name="t1_enport">t1_port</string>
<string name="t1_1_en_port">t1_1_port</string>
</resources>
// values-en-port/strings_en_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-port</string>
<string name="t1_1_en_port">t1_1_en_port</string>
</resources>
在R.java中生成的资源ID如下:
public static final class string {
public static final int teststring_all=0x7f050000;
public static final int testport_port=0x7f050004;
public static final int t1_enport=0x7f050001;
public static final int t1_1_en_port=0x7f050002;
public static final int t2=0x7f050003;
}
由此,你可以看出,尽管定义了很多字符串,但是在R.java中只生成了五个ID。现在,使用这些字符串时的表现记录如下:
n teststring_all:这个ID在五种变化的值目录下都有。而在values-en-rUS目录下的被选取了。
n 基于优先级的规则,指定了语言的优先于default,en,port和en-port变化。
n testport_port:这个ID在default和-port中存在。因为它没有出现在任何以-en开头的目录下,所以-port将优先于default。如果有一个在-en变化中,这个值就将冲-en中选取。
n t1_enport:这个ID出现在default,-en和-port。因为-en和-port同时存在,所以将会在-en中选取。
n t1_1_en_port:这个ID出现在四种变化中:default,-port,-en,和-en-port。将会从-en-port中选取。
n t2:这个只在default中出现,所以值将从default中选取。
Android SDK已经给出了更详细的算法,你可以加以阅读。然而,本章中的例子表述了它的本质。关键是了解某种变化与其它变化的优先级的先后顺序。我们提供了一些链接地址以供参考。
链接地址
做为学习Android资源的参考,你也许可以参考如下地址。
http://developer.android.com/guide/topics/resources/index.html:
这个文档是资源的详细介绍
http://developer.android.com/guide/topics/resources/availableresources.
html:Android文档,对于各种资源都进行了介绍
http://developer.android.com/reference/android/content/res/Resources.html 你将在这里找到读取各种资源的方法
http://developer.android.com/reference/android/R.html: 你可以看到Android平台中核心资源的定义
http://www.androidbook.com/item/3542: 你将能找到复数,字符串数组,以及备用资源。
http://www.androidbook.com/projects: 你可以使用这个地址下载本章的Eclipse工程,工程名称为ProAndroid3_Ch03_TestResources.zip.
总结
我们迅速列举出本章的知识点。您知道了Android支持的资源类型,您知道如何在XML中建立这些资源。您知道了如何生成资源ID以及如何在Java代码中使用它们。您还了解到,资源ID是一种便捷的使用资源的方案,简化了Android对于资源的使用。最后,你学习了如何使用原生资源与固定资源。我们也简要地谈到了替代资源,复数以及字符串数组。就这样,我们将注意力转向下一章——内容提供者。