第二章中,我们简要介绍了一些android应用程序的结构和基本概念。同时,你也已经了解了Android SDK、Eclipse的开发工具ADT,如何使用虚拟机运行android程序。
     在本章和接下来的几章,我们会深入了解一下Android SDK的基础模块。这些模块包括资源(resources)、content providers和intents。
     Android依赖资源对UI控件进行描述性的定义。这种定义与HTML使用tag定义UI没有什么不同。基于这种思想,Android对UI设计方法进行了改进。Android运行资源模式化、局部化。本章将介绍各种Android中的资源。
     理解资源
     资源在Android架构中扮演了重要角色。Android中的资源是与可运行应用所绑定的文件(类似于音乐文件或者描述窗口部局的文件)或者值(例如对话框的标题)。这些文件与值的绑定方式可以允许在改变时不必重新编译应用。
     资源的一些常见例子包括字符串(Strings)、颜色(colors)、位图(bitmaps)和布局(layouts)。资源中的字符串允许你使用其资源id,而不是在代码中进行硬编码。这种间接使用资源的方法可以使你只改变资源而不用改变代码。
     Android中有相当多的资源。我们就以最常见的资源String为例进行介绍。
     字符串资源
     Android允许你在一个或者多个xml资源文件中定义字符串。这些包含字符串的资源文件位于res/values目录下。虽然你常见的是名字为strings.xml的文件,但是事实上其名称可以是任意的。列表3-1给出了一个字符串资源的例子:
     Listing 3-1:string.xml
<?xml version = "1.0" encoding="utf-8"?>     hello     hello appname
     注:在一些eclipse版本中,需要xmlns进行限制。只要有xmlns描述就可以,而不必管xmlns到底指向哪里。例如下面的两个例子都可以:          
     甚至连第一行(指明这是一个xml文件,其编码格式是什么)都可以忽略,Android依然可以很好的运行。
     当该文件创建或者进行更新时,Android的ADT工具会自动在根目录的包中生成一个R.java文件,其中不同的字符串有着不同的id号。注意下面例子里R.java的位置。这是一个应用的高层面的结构:Pro Android 4 第三章 理解Android中的资源_第1张图片
          注:无论有多少资源文件,只要一个R.java文件生成。
     对于列表3-1中的资源,在R.java中对应的结构如下:     Pro Android 4 第三章 理解Android中的资源_第2张图片
     请注意android是如何定义顶层类的:public final class R。其内部类为public static final类型,名为string。R.java文件通过创建这个内部类作为string的命名空间,来统一管理string资源。
     其中的两个static final的int数据是与之相对应的字符串资源的id。你可以通过下面的形式在任何地方使用资源id:
     R.string.hello
     这些自动生成的id指向的是int类型,而不是string类型。大多数方法使用字符串时,以这些int型的id作为输入,android会在需要时将这些int数据转换为string。
     将所有的字符串定义在strings.xml里仅仅是一个约定俗成的规范。任何文件,只要其内部结构类似于list 3-1,且位于res/values目录下,android都可以进行处理。
     这个文件的结构很容易学习。你会有一个根节点,里面有一系列子元素。每一个子元素里都有一个name属性,与R.java里的id相对应。
     为了演示android允许多个字符串资源文件存在,你可以加入另一个资源文件string1.xml,放在同样的目录下,其内容见list 3-3
     Pro Android 4 第三章 理解Android中的资源_第3张图片
     android ADT插件会在编译时检查id的唯一性,然后把这些id作为新的常量加入到R.java里去,例如:R.string.hello1和R.string.app_name1。
     布局资源
     在Android中,屏幕上显示的图像往往从xml文件中加载而来,而xml文件则视为资源。这与HTML文件描述网页的内容与布局十分相似。这些xml文件称为布局资源。布局文件是Android UI编程中的关键性文件。以list 3-4展示的代码为例:Pro Android 4 第三章 理解Android中的资源_第4张图片
     
     setContentView(R.layout.main)这一行可以看出,存在着一个静态的类R.layout,其内部有一个常量main(int 类型)指向了一个由布局资源文件所定义的视图。该布局文件的名称为main.xml,位于res/layout目录下。换句话说,这个表达式需要程序员创建一个文件res/layout/main.xml,并在文件中定义必要的布局。main.xml的内容可以如List3-5所示:
     Pro Android 4 第三章 理解Android中的资源_第5张图片

     文中定义了一个名为LinearLayout的根节点,其中包含着一个TextView和一个Button。LinearLayout可以以水平或者垂直的方式排列子布局。本例为垂直方式。
     你需要为每个屏幕(或activity)定义一个单独的布局文件。更确切的说,每一个布局需要对应一个专门的文件。如果你打算画两个屏幕,你很可能需要两个布局文件:res/layout/screen1_layout.xml和res/layout/screen2_layout.xml。
     注:res/layout目录下的每一个文件都会根据其文件名(不包括后缀)生成一个独一无二的常量。对于layout而言,布局文件的数量是关键,而对于string资源,单独的string资源的数量才是关键所在。
     例如你在res/layout有两个layout文件,分别为file1.xml和file2.xml。这样在R.java里会生成如List3-6所示的结构:
     Pro Android 4 第三章 理解Android中的资源_第6张图片
     资源文件中的具体视图,例如TextView等可以通过R.java中自动生成的资源id进行引用,如     TextView tv = (TextView) findViewById(R.id.text1);     tv.setText("Try this text instead");
     本例中,你通过Activity类中的findViewById方法来定位到TextView。其中常量R.id.text1与TextView中定义的id相对应。如:          ...          
     其中,属性id的值,也就是常量text1被用来唯一标识该TextView,和Activity中的其它View区别开来。“+”表示如果id “text1”不存在的话则创建一个。下面你会看到更多的关于id属性的内容。
     资源引用符(Resources Reference Syntax)
     先不考虑各种资源的类型(目前为止我们已经接触到了string和layout两种),Android的java代码中是通过其id来引用资源的。在xml文件中,你所使用的将id与资源向相联系的符号被称作Resources reference syntax。这种语法并不局限于通过id来进行资源定位,它是一种定义string、layout和image等资源的方法。
     这种通用的方法是如何实现资源定位的呢?通过其用法可以看出,id其实就是一个能追踪类似string等资源的数字。想象一下,你的工程里有一桶数字,你可以从中挑选一个来使之与某个控件相关联。
     让我们进一步发掘一下这种资源引用的结构。这种资源引用有如下结构:
     @[package:]type/name
     type与R.java中的资源类型的命名空间相对应。例如:
     R.drawable     R.id     R.layout     R.string     R.attr     R.plural     R.array
     而与之相对应的资源引用符号如下:     drawable     id     layout     string     attr     plurals     string-array          而name则是这些资源的名称。(例如list3-5中text1)同时,在R.java中它也代表着一个int型常量。
     如果不指定package名字,则type/name会基于本地资源以及本地生成的R.java文件。
     如果其形式为android:type/name,则引用会基于android包以及android.R.java。你可以使用任何java的包名来作为package,这样与之相对应的R.java文件会处理相关引用。
     现在让我们在回头看看List3-5中id是如何与一个空间相关联的。id被视为资源,通过这个逻辑,当你写下:
            //编译错误,因为id不能被赋值为一个字符串。更重要的是它不是一个资源引用符号
           // 语法错误。@text还缺少类型名。应该写为@id/text,或@+id/text,或@string/text。不过string在此处并不合法,尽管这是一个合法的资源引用符号。这是因为左边需要的是一个id,而不是string。所以你会得到一个错误信息“No Resource type specified”
           //错误,因为无法找到与text相匹配的资源。当然,除非你之前已经定义了名为text的资源
            //错误,该资源为非public资源。表明在android.R.id中无此id。当然,假设在Android.R.java中定义了text,则为合法
            //成功。在本地包中R.java里创建一个名为text的id
     在“@+id/text”中,“+”拥有特殊的含义。它告诉Android 名为text的id不一定存在,如果果真如此,则创建一个名为text的id。除了在id中,在一般的资源引用符号里我们不会使用“+”。这是很有道理的,因为你无法想象一个场景,string资源没有被显示定义就被创建。系统无法自动创建,因为它必须是一个独一无二的数。            这种资源与id直接的连接关系常常是造成困扰的源头。为了解决这个问题,请记住一件事:id就是对资源的一个引用。
     定义你自己的资源id以便日后使用
     配置id的一般形式是创建一个新的id或者使用Android包里的id。其实,也可以自己先创建id,然后在自己的包里使用id。这一切都基于id就是资源的事实。如果id是资源,那么就可以被预先定义好,并在以后使用。
     List3-7中表明:一个名为text的id,如果已经存在,则可以被使用。如果不存在,则会创建一个。所以,当text已经存在时,会复用它吗?
     你可能倾向于在R.java中放一个如R.id.text的常量,但是R.java是不可编辑的。即使可以编辑,该文件也会在res目录下的文件发生变化、增加或删除时重新生成。
     解决办法是使用一个名为item的标签来定义id,而不必将id与任何资源绑定。List3-8就是一例:      Listing 3-8 预定义id                  
     本例中,type为id类型。一旦这个id存在,则Listing3-9就正确了。
     Listing3-9 复用一个id            ...      
     编译与反编译Android的资源
     Android主要有两种资源文件:xml文件和raw文件(如音频、视频和图片)。你已经看到,在某些文件资源被定义为xml文件中的value(如string),而某些文件本身就是一种资源(如layout文件)。
     xml文件之间更为显著的区别是:一种文件被编译成为二进制文件,而另一种被原封不动的拷贝到设备里。目前你所看到的了两个例子(string资源文件和layout资源文件)都是先编译成二进制文件,然后打包到安装包里。这些xml文件已经预定义好了格式,xml的节点被转化为id。
     你也可以选择使一些xml文件拥有自定义的格式,这些文件不会被解释,但是可以生成id。(资源类型为xml)。然而,你确实需要将其编译为二进制文件,并享有易于定位的便捷。为了这个目的,你可以讲文件放在/res/xml目录下。这样,你就可以使用Android提供的xml阅读器来读取xml节点。
     但是,如果你将文件放在res/raws目录下,包括xml文件在内都不会被编译成二进制文件。然而,其毕竟是资源文件,因此Android在R.java中生成一个id。其资源类型为raw,所以你可以通过R.raw.name(name去掉扩展名)来进行访问。你必须显式的使用基于流的api对这些raw文件进行读写,音频和视频文件正属于这个类型。
     注:由于raw目录也是res/目录的一部分,因此音频和视频文件和其它资源文件一样也可以通过id进行定位。
     正如我们在第二章的表2-1所示:资源文件根据其类型放在不同的子目录下。下面是一些重要的子目录及其所放的文件类型:            anim:已经编译好的动画文件。      drawable:位图      layout:ui和view的定义      values:Arrays、color、dimensions、strings和styles      xml:编译好的xml文件      raw:不经编译的文件。
     Android Asset Packing Tool(AAPT)中的资源编译器将除了raw目录下的资源文件编译到最终的.apk文件中。该文件包含了Android应用的代码和资源,以及相关联的jar包。(apk表示Android package)最终,这个.apk文件会被安装到设备中。
     注:尽管xml资源解析器允许资源名为hello-string的形式,但是在R.java中会有编译时错误。你可以通过把破折号改为下划线来解决此问题,如hello_string。
     列举一些Android的关键资源
     现在你已经了解了一些基本的资源文件。我们将进一步了解一下其他的Android支持的资源文件,包括其xml表示形式已经如何在代码中使用。(当你编写资源文件时,可以用本节内容作为快速参考手册)。让我们先快速浏览一下资源类型及其类型,如表3-1所示:
     Table3-1:资源类型
资源类型 位置 描述
Colors /res/values/any-file 颜色标示符,其id在R.java里表示为R.color.*。xml节点为Resources/color
Strings /res/values/any-file 表示字符串资源。除简单字符串外还支持java格式化字符串和纯HTML。其id在R.java里为R.string.*。xml节点为Resources/string
String arrays /res/values/any-file 表示字符串数组。其id在R.java里为R.array.*。xml节点为resources/string-array
Plurals /res/values/any-file 表示一定数量的字符串集合。在不同的语言环境下,你写一个句子的方式取决于你引用了0个、1个、一些或者很多对象。其id在R.java里为R.plural.*xml节点为resources/plurals
Dimensions /res/values/any-file 表示不同view的尺寸大小。支持pixels像素,inches英寸,millimeters毫米,density independent pixel密度无关像素dp和scale independent pixels尺寸无关像素sp。其id在R.java里为R.dimen.*。xml节点为resources/dimen
Images /res/drawable/multiple-files 表示图像资源。支持图片格式为jpg/png/gif等。每个图像是一个单独的文件,其id取决于图片名称。其id在R.java里为R.drawable.*。支持的图像类型还包括可伸缩的图像:其图像一部分可以伸缩,而另一部分保持不变。可伸缩文件就是常说的.9.png文件
Color drawables /res/values/any-file
/res/drawable/multiple-files
表示常备用来作为背景的矩形颜色或一般的位图。其可以代替用作背景的单一颜色的位图。在java中,其等价于创建一个带颜色的矩形并设置为背景。
在values子目录下的值标签支持该类型。其id在R.java里为R.drawable.*。xml节点为resources/drawable。
Android也支持圆角或渐变的矩形,这些文件以标签作为根标签,放在res/drawable目录下。其id在R.java里也为R.drawable.*。其文件名被转换为独一无二的id名。
任意xml 文件 /res/xml/*.xml Android支持任意类型的xml。这些xml被AAPT编译为二进制文件。其id在R.java里为R.xml.*。
任意raw资源 /res/raw/*.* Android允许将不会再被编译的二进制文件或文本文件放在此目录下。每个文件都对应唯一的id。其id在R.java里为R.raw.*。
任意raw assets /assets/*.*/*.* Android允许将任意类型的文件房子啊assets目录下任意层级的子目录下。这些并不是真正的资源,仅仅是纯文件。该目录与res目录不同,允许任意深度的子目录。这些文件不会产生相应的id。你只能用一个相对路径名(相对于/assets,但不包含改路径)


     表中的所有资源都会在后面的章节中用xml文件和java代码片段详细描述。            注:通过观察id的生成方式,我们可以看到(尽管没有任何官方说明):res/values目录之外的资源文件的id都是根据文件名生成的,而res/values目录下的资源id生成,则需要进入到资源文件里面根据其内容生成。
     String Arrays
     你可以设置一个字符串数组作为资源,将其置于res/values目录下的任意文件中。你需要使用string-array作为xml的节点。这个节点是xml节点的一个子节点,类似于string节点。Listing3-10是一个在资源文件中定义字符串数组的例子:
     Listing3-10 定义字符串数组
           .......Other resources                 one           two           three            ......other resources      
     一旦你定义了这样一个字符串数组,就可以在java文件中如Listing3-11这样使用:
     Listing3-11 使用字符串数组
      //从Activity中获取资源对象      Resources res = your-acitivity.getResources();      String[] strings = res.getStringArray(R.array.test_array);      // 打印字符串      for(String s : strings) {           log.d("example", s);      } 
     复数
     plurals资源是一个字符串集合。这些字符串根据某个数量的不同有不同的表示形式。以一个窝里有多少鸡蛋为例:            There is 1 egg;      There is 2 eggs;      There is 0 eggs;      There is 100 eggs;
     注意,当数字为0、1、100时这几个句子的不同之处。其中,当数量为1时,其句子有所不同。Android通过plurals资源来支持这种变化。Listing3-12将告诉你如何使用plurals来实现字符串根据数量不同而发生改变:            Listing3-12:应用复数                       There is 1 egg           There are %d eggs            
     两种不同表达式在同一个plurals里。现在你可以在java代码里通过赋予一个数量值来使用这个plurals资源。如Listing3-13所示。getQuantityString()的第一个参数时plural资源的id。第二个参数选择使用哪个字符串。当其值为1时,你使用的是该字符串本身。当字符串不是1的时候,你必须制定第三个参数来替换%d.如果你在plurals里使用格式化字符串,则你应该至少传入3个参数。第二个参数可能造成混淆,其实其关键在于该值是否等于1.            Listing3-13      Resources res = your-acitivity.getResoureces();      String s1 = res.getQuantityString(R.plurals.eggs_in_a_nest_test, 0, 0);      String s2 = res.getQuantityStirng(R.plurals.eggs_in_a_nest_test, 1, 1);      String s3 = res.getQuantityStirng(R.plurals.eggs_in_a_nest_test, 10, 10);
     通过上述代码,会根据传入的值不同,而返回合适的字符串。
     然而,如果还有其他的数量属性该如何处理呢?我们强烈推荐你阅读一下Android的Resource.java和Plurals.java源码来真正理解这个问题。本章末尾关于Resourecs的连接中给出了这两个文件的地址。
     其底线在于,对于英文环境,只有两种环境“one”和“others”。这对于其他大多数语音也是如此,但是对于cs(Czech)语言来说就不是这样了,捷克语有三种情况“one”(1个),“few”(2-4个)和“others”(剩余情况)。
     更多关于字符串数组的内容
     前面几节你已经简要了解了字符串数组的相关内容。我们重新审视一下这些内容并给出它们之间的细微差别:包括HTML字符串以及如何替代字符串资源中的变量。
     注:大多数UI框架都支持字符串资源。然而,Android允许通过资源id来对字符串进行索引,因此更为简单。
     Listing3-14将告诉你如何在xml资源文件中定义普通字符串、带引号字符串、HTML字符串和可替换字符串:            Listing3-14:利用xml语法定义字符串
                "quoted 'xyz' string"           \"double quotes\"            hello %2$s Java format string. %1$s again           Hello Slanted Android, You are bold.      
     xml字符串资源文件需要放在res/values目录下,其文件名可以任意。            带引号字符串需要通过转义字符输出双引号。字符串还允许以java格式化字符串的形式进行定义。                 Android还允许在string节点内部使用xml的子节点,如>及其他简单的HTML字体标记。你可以通过复合的html字符串来定义的字符的样式以便在textview中输出。
     Listing3-15表示如何在java代码中使用这些字符串:
     Lisint3-15.在java代码中使用字符串资源
     // 使用简单的字符串,并在textview中显示      String simpleString = activity.getString(R.stirng.simple_string);      textView.setText(simpleString);
     // 读取一个带引号的字符串,并在textview中显示      String quotedString = acitivity.getString(R.string.quoted_string);      textView.setText(quotedString);
     // 读取一个带双引号的字符串,并在textview中显示      String doubleQuotedString = activity.getString(R.string.double_quoted_string);      textView.setText(doubleQuotedString);
     // 读取一个java格式化字符串      String javaFormatString = acitivity.getString(R.string.java_format_string);      // 通过传入参数格式化字符串      String substitutedString = String.format(javaFormatString, "hello", "Android");      textView.setText(substitutedString);
     // 读取一个html字符串,并显示      String htmlString = activity.getString(R.string.tagged_string);      // 将其转换为span,这样就可以显示在textview      // android.text.html类支持html字符串,这仅仅是一个android的内部类,不支持所有的html标签      Spanned textSpan = android.text.html.fromHtml(R.string.tagged_string);      textView.setText(textSpan);
     一旦你定义好一个字符串资源,就可以在xml的layout文件里直接使用,例如在TextView里直接设置。Listing3-16列出如何在TextView里直接使用html字符串。
     Listing3-16:xml文件中使用字符串资源      
     TextView自动识别出该字符串是html字符串,并依此进行格式化。你可以直接在layout中的View中快速设置漂亮的文字格式是件非常棒的事情。
     颜色资源Color Resources
     和字符串资源一样,你也可以使用资源引用符来间接的引用颜色资源。这使得Android可以讲颜色资源本地化,并且可以提供主题。一旦你在资源文件中定义了颜色资源标示符,你就可以在java代码中通过其id引用颜色资源。对比字符串资源,其id位于<你的包名>.R.string命名空间中,颜色资源位于<你的包名>.R.color命名空间中。
     Android在其自带的资源文件中也定义了一套基本的颜色。这些id位于android.R.color命名空间中。通过下面地址可以进一步学习android.R.color中可以获取的颜色常量:       http://code.google.com/android/reference/android/R.color.html            Listing3-17列出了一些在xml资源文件中定义color资源的例子:
     Listing3-17:定义color资源的xml语法                 #f00           #0000ff           #f0f0           #ffffff00      
     Listing3-17的内容需要为与res/values目录下。其文件名可以任意选取。Android会扫描所有文件,从中选取节点来计算出其id。
     Listing3-18列出如何在java代码中使用color资源。      Listing3-18:      int mainBackGroundColor = activity.getResources().getColor(R.color.main_back_ground_color);
     Lisint3-19列出如何在定义View时使用color资源。
     Listing3-19:      
     尺寸资源dimension resources
     像素、英寸、点都是尺寸的一种,均可以在xml文件和java文件中使用。你可以用这些尺寸资源样式化和本地化Android的UI而不必修改java代码。
     Listing3-20列出如何在xml文件中使用尺寸资源            Listing3-20:                 1px           1dp           100sp      
     你可以用以下任意单位定义尺寸:            px:像素      in:英寸      mm:毫米      pt:点      dp:像素无关像素。基于dpi(每英寸像素)为160的屏幕而定义。尺寸根据屏幕密度而动态变化。      sp:缩放无关像素.允许用户调整大小的尺寸。主要用于字符。
     在java中,你需要实例化一个资源对象来获取尺寸。你可以在activity中调用getResourece()方法,然后根据该资源实例和尺寸资源id来获取实际尺寸值。
     Listing3-21:在java代码中获取尺寸值            float dimen = activity.getResources().getDimension(R.dimen.mysize_in_pixels);
     注:在java中需要调用Dimension的全称,而在R.java命名空间中选取的是其缩写dimen。
     相对于java代码,在xml中使用尺寸资源,需要使用简写dimen,见Listing3-22
     Listing 3-22:在xml文件中使用尺寸                 

     图像资源Image Resources
     Android会为存储在res/values目录下的图像资源生成资源id号。支持的图像类型为.gif、.png和jpg。每个图像资源会根据其名称生成独特的id号。如果一个图像的文件名称为sample_image.jpg,则生成的id号为R.drawable.sample_image。
     注意:如果有两个文件的名称相同会得到错误提示。res/drawable下面的子目录下的图像会被忽略。该目录下的任何文件都不会读取。
     你可以在其他的xml文件中引用res/drawable的图像资源,如Listing3-23所示:      Listing 3-23在xml中使用图像资源