android:right="10dp" android:bottom="10dp"/>
你可以将这个图像用作前面的textview的背景,如Listing3-29所示:
Listing3-29:在java代码中使用drawable
// 获取drawable
GradientDrawable roundedRectangle = (GradientDrawable) activity.getResources().getDrawable(R.drawable.my_rounded_rectangle);
// 设置drawable为TextView的背景
textView.setBackgroundDrawable(roundedRectangle);
注:并不一定要吧返回的Drawable基类转为GradientDrawable,这么做只是为了展示标签会转换为GradientDrawable。这个信息很重要,因为你可以查阅一下该标签定义的类的API文档。
最后在说明一点:drawable子目录下的文件对应着一个BitmapDrawable类。一个drawable资源值,如Listing3-25中的矩形对应着ColorDrawable。而包含标签的xml文件对应着GradientDrawable。
使用任意xml资源文件
除了之前介绍的结构化的资源,Android还允许将任意的xml文件作为资源。这种方法将对资源的使用扩展到了对任意xml文件的使用。首先,这种方法通过使用以这些文件为基础而生成的资源id来很方便的使用它们。第二,这种方法允许你能本地化这些xml资源文件。第三,你可以有效的编译和在设备上存储这些xml文件。
允许通过这种方式读取的xml文件存储在res/xml目录下。Listing3-3-列出了一个名为res/xml/test.xml的例子:
Listing 3-30:xml文件举例
Hello World from a xml sub element
正如其他xml资源文件一样,AAPT先将该文件编译,然后再放到应用包里。如果你想解析这些文件,你需要一个XmlPullParser实例。你可以使用Listing3-31中的代码获取一个XmlPullParser的实例(可以在任意Context下使用):
Listing 3-31:读取一个xml文件
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
返回的XmlResourceParser是XmlPullParser的一个实例,它还实现了java.util.AttributeSet接口。Listing3-32列举了更多的读取text.xml文件的代码片段:
Listing3-32:使用XmlPullParser
private String getEventFromAnXmlFile(Activity activity) throws XmlPullParserException, IOException {
StringBuffer sb = new StringBuffer();
Resources res = activity.getResources();
XmlResoucesParser 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
从Listing3-32,你可以知道如何得到一个XmlPullParser,如何使用XmlPullParser来遍历xml文件中的元素,如何使用XmlPullParser中的方法来获取元素的细节信息。如果你想运行上述代码,你需要创建一个之前介绍的xml文件,然后通过菜单或按钮调用getEventFromAnXmlFile方法。你可以通过Lod.d调试方法将返回的字符串打印出来。
使用Raw资源
除了xml文件外,Android还允许使用raw文件。这些资源位于res/raw目录下。这些raw文件可以是音频、视频或txt文档。它们都可以通过资源id来进行本地化或者引用。与在res/xml目录下的xml文件不同,这些raw文件不会被编译,但是会打包到应用包里对应的位置。然而,每个文件都会在R.java文件中生成相应的标识符。如果你创建了一个res/raw/test.txt的文件,那么可以使用Listing3-33中的代码读取该文件。
Listing3-33:读取raw资源
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 ByteArrayOutputSteam();
int i = is.read();
while(i != -1) {
baos.write(i);
i = is.read();
}
return baos.toString();
}
注:如果文件名称是通过复制自动产生的,则ADT会产生一个编译错误。这是所有基于文件名称产生资源id的情况共有的问题。
使用Assets
Android还提供了其它的一些目录用来存放文件,如/assets。其与res属于同一级别目录。也就是说assets并不是res下的子目录。在assets目录下的文件不会再R.java中生成资源id。你必须指定路径来读取这些文件。文件路径是基于/assets的相对路径。如Listing3-34所示,你可以使用AssetManager来读取文件:
Listing3-34:读取asset
//注:本断代码未进行异常处理
String getStringFromAssetFile(Activity activity) {
AssetManager am = activity.getAssets();
InputStream is = am.open("test.txt");
String s = convertStreamToString(is);
is.close;
return s;
}
复习一下资源目录的结构
Listing3-35展示了所有资源的目录结构:
Listing3-35:资源目录
/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/*.*/*.*
注:由于没有位于res目录下,只有asset下面的目录可以有任意层次的子目录。其他所有的目录只能包含文件,而不能有子目录。这也与R.java为这些资源生成id的方式相符。
资源及配置
资源有利于本地化。例如:你可以设置不同的语音环境来得到不同的字符串。Android资源可以根据不同的配置发生变化,而语言仅仅是其中的一种。另外一种配置的变化是设备由垂直状态变为水平状态。垂直模式被称为portrait模式,而水平模式被称为landspace模式。
Android允许你通过同一个id来引用不同布局模式的布局。这可以通过为不同的配置设置不同字母来实现。Listing3-36就是一例:
Listing3-36:可替换的资源目录
res/layout/main_layout.xml
res/layout-port/main_layout.xml
res/layout_land/main_layout.xml
即使有三个不同的文件,也只会在R.java中生成一个id。其id形如:
R.layout.main_layout
然而,当你使用这个布局id时,当配置不同你会得到相对于的布局。
本例中扩展的-port和-land被称为配置限定词(configuration qualifiers)。这些限定词是大小写敏感的,并且用“-”与资源隔开。通过这种限定词定义的资源被称为可替换资源。(alternate resources)而res目录下没有configuration qualifiers的资源被称为默认资源。
下面列举了绝大多数configuration qualifiers。注意新的qualifier可能加入到新的APIs中。可以通过参考部分的网址来查看最新的资源qualifier。
mccAAA:AAA代表mobile country code.
mncAAA:AAA代表carrier/network code。
en-rUS:语言和地区
swdp,wdp,hdp:分别表示smallest width,available width,available height。(API 13)
small、normal、large、xlarge:屏幕尺寸
long、nolong:屏幕类型
port、land:portrait和landspace。
car、desk:docking的类别
night、notnight:夜间或白天
ldpi、mdpi、hdpi、xhdpi、nodpi、tvdpi:屏幕密度
notouch、stylus、finger:屏幕种类
keysexposed、keyssoft、keyshiden:键盘种类
nokeys、qwerty、12key:按键数量
navexposed、navehidden:导航键隐藏或显示。
nonav、dpad、trackball、wheel:导航设备类型
v3、v4、v7:API级别
有了这些qualifiers,你可以像Listing3-37所示定义资源目录:
Listing 3-37:
res/layout-mcc312-mnc222-en-rUS
res/layout-ldpi
res/layout-hdpi
res/layout-car
你可以通过Costom locale 应用来查看你当前的语言环境。该应用的路径为:Home -- List of Applications -- Costom locale
当给定一个资源id好,Android会用一个特定的算法来获取相应的资源。你可以通过参考“Reference URLs”章节来查找相应的地址来获取更多的内容,不过现在我们先看一下其中的一部分规则。
主要原则是最先列出的qualifier拥有最高的优先级。考虑一下Listing3-38列出的路径:
Listing 3–38. Layout File Variations
\res\layout\main_layout.xml
\res\layout-port\main_layout.xml
\res\layout-en\main_layout.xml
在列表中main_layout.xml有两个不同的变种。一个是语言相关而另一个是方向相关。现在我们检验一下,当手机处于垂直状态时,会选择哪个layout。结果是即使手机处于垂直状态,系统扔选择了语言相关的layout。这是因为语言的qualifier的优先级高于方向的qualifier。本章中“Reference URLs”章节的连接中列出了所有的qualifier和优先级。
我们通过几个字符串资源做实验来进一步探讨一下这种优先级规则。注意字符串资源是基于独立的id号而layout资源是基于文件的。为了测试字符串资源的configuration qualifier优先级,我们使用5个资源id分别对应于5个变种:default、en、en_us、port和en_port。这5个id资源是:
teststring_all:这个id在所有的资源目录变种中都存在,包括默认的资源。
testport_port:这个id只在default和-port目录下。
t1_enport:这个id在default、-en和-port目录下。
t1_1_en_port:这个id在default和-en-port目录下。
t2:只在default目录下。
Listing3-39列出了value目录所有的变种:
Listing 3–39. String Variations Based on Configuration
// values/strings.xml
teststring in root
testport-port
t1 in root
t1_1 in root
t2 in root
// values-en/strings_en.xml
teststring-en
t1_en
t1_1_en
// values-en-rUS/strings_en_us.xml
test-en-us
// values-port/strings_port.xml
test-en-us-port
testport-port
t1_port
t1_1_port
// values-en-port/strings_en_port.xml
test-en-port
t1_1_en_port
Listing3-40列出了R.java中生成的id:
Listing 3–40. R.java to Support String Variations
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;
}
马上你就会看到,即使你定义了无数的String,但是只有5个string资源id生成。如果你使用这些字符串资源,每个字符串资源的具体行为如下:(我们测试的配置是:en_US和portrait)
teststring_all: 这个id有5个不同值。因为在5个目录下均存在,最终会选择values-en-rUS/目录下的值。基于这种优先级规则,特定的语言优先级高于default、en、port和en-port。
testport_port : 这个id只在default和-port目录下。因为没有在任何以-en开始的目录下,-port目录优先级高于default,所有-port目录下值被选取。如果其在-en目录下,则会选取-en目录下的值。
t1_enport : 这个id在3个目录下:default、-en和-port。由于同时在-en和-port下,所以会取-en下的值。
t1_1_en_port:这个id在4个目录下:default、-en、-port和-en-port。因为可以在-en-port目录下获取,所以忽略-en、-port和default。
t2:只在default目录下,所以从default目录取值。
Android SDK还有一个更为细致的算法规则,你可以仔细研究。然而,这个实验已经告诉了你它的本质。其关键在于不同变种的优先级。接下来的部分提供了一些URL。其中包括一个地址来下载本章的一个移植的工程。你可以通过这个工程来检测不同配置。
Reference URLs
当你学习Android的资源时,你可能需要将下面的参考网址保存起来。除了列出网址外,我们还标明了相应网址的内容:
http://developer.android.com/guide/topics/resources/index.html:
资源文件相关文档的索引。
http://developer.android.com/guide/topics/resources/available-resources.html
Android关于资源类型的文档。
http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources :
最新Android SDK中configuration qualifier列表。
http://developer.android.com/guide/practices/screens_support.html:
关于如何适应各种尺寸的屏幕的指导文档。
http://developer.android.com/reference/android/content/res/Resources.html:
读取资源的各种方法。
http://developer.android.com/reference/android/R.html :
Android核心平台的资源定义
www.androidbook.com/item/3542 :
我们关于复数plural、字符串数组string array和可变资源alternative resources的研究,也包括一些其它资源的链接。
androidbook.com/proandroid4/projects:
本章中涉及到各种资源的工程下载。其名为ProAndroid4_Ch03_TestResources.zip.
概要
我们简要总结一下本章学到的内容:
你知道了Android支持的资源类型。
你知道如何在xml文件中创建资源。
你知道了资源id是如何生成的,并且知道如何在java代码中使用资源id。
你知道如何使用xml 资源、raw资源和assets。
简单地接触到了可变资源。
你知道如何定义plural和string array
你学会了资源引用语法。
复习问题
你可以用下面的问题作为知道来巩固你所学到的知识:
1.你能说出多少种类型的资源。
2.什么是R.java
3.为什么R.java为使用资源提供了便利。
4.资源定位符resource-reference syntax和为控制UI分配的id之间的联系什么
5.文件的扩展名是否会被用来生成资源id?
6.如果两个基于文件名的资源在一起,而两种的区别仅在于扩展名,那么会发生什么情况?
7.什么是raw资源和xml资源,其与assets之间的区别是什么?
8.你能定位到xml资源吗?
9.你能定位到assets吗?
10.你能写出并解释resource-reference syntax吗?
11.你能提前定义资源id以便使用吗?如果可以,请问为什么?
12.创建一个id需要什么xml节点。
13.如果你将文件放在xml或者raw目录下,会在R.java中生成相应的id吗?
14.Android会为assets目录下的文件生成id吗?
15.plurals中的one和others代表什么。
16.你能在字符串中使用HTML字符串吗?
17.你如何在一个textview中显示HTML字符串
18.你如何将一个矩形定义为drawable
19.你如何使用一个shape drawable?
20.你需要使用哪个类来读取res/xml目录下的xml文件。
21.在Android中,主要用来处理xml文件的类是哪个?
22.什么是AssetManager,你如何得到它?
23.什么是资源类,你如何得到其实例?
24.你能在assets目录下有任意层级的子目录吗?
25.你能在res/xml目录下设置子目录吗?
26.什么是resources configuration qualifiers?
学会了这些,现在我们将把注意力转到下一张的content provider上来。