Pro Android 4 第三章 理解Android中的资源

     第二章中,我们简要介绍了一些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

     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,名称为text1,将text1作为TextView实例的一个引用”。其中“+”表示:如果text1还没有定义为一个资源,则用一个与众不同的数字进行定义。

     基于上述信息,我们来分析一些id的例子。通过List3-7我们可以看出,“android:id”并非该语法的一部分,它只是将id与控件(如TextView)连接的一种方法。

     Listing3-7:

     
      //编译错误,因为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语法定义字符串

     
                     
          \"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文件中使用字符串资源
     
                      android:layout_height="wrap_content"
                      android:gravity="center_horizental"
                      android:text="@string/tagged_string"/>

     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:
     
                      android:layout_height="wrap_content"
                      android:textColor="@color/red"
                      android:text="Sample text to show red color"/>

     尺寸资源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文件中使用尺寸
          
     
                      android:layout_height="wrap_content"
                      android:textSize="@dimen/medium_size"/>


     图像资源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中使用图像资源
     

你可能感兴趣的:(Pro,Android,4,翻译)