第九章 多语言环境的支持和多屏幕的适配
资源是在代码中使用到的,并且在编译时被打包进应用程序的附加文件。出于加载效率的考虑,资源被从代码中分离出来,而且XML文件被编译进二进制代码中。在Android中,程序代码可以不直接和资源发生关系,而是通过R文件提供的索引来间接的引用某一个资源。Android系统会自动根据用户当前的环境,和屏幕分辨率情况,自动选用最合适的资源。正是基于Android系统这种独特的处理方式,开发者可以编写多套资源文件,从而很方便的实现多语言环境的支持和多种屏幕的适配。
本章将详细说明Android程序的资源的结构,以及具体如何适配多语言环境和多种屏幕。
9.1Android程序的资源文件
9.1.1资源文件的目录结构
资源是外部文件(即非代码文件),它在代码中被使用,是在编译的时候加载到应用程序的。Android支持很多种不同类型的资源文件,包括XML、PNG和JPEG等文件。在Eclipse的工程中,res目录有默认几项resource,比如:
1)res/drawable/---图像类型的资源文件。
2)res/layout/---可被编译成屏幕布局(部分布局)的XML文件。
3)res/values/---可被编译成多种类型的资源的XML文件。文件可以被命名为任何名字,这个文件夹有一些经典的文件(一般约定以文件中定义的元素名称来给文件命名):
arrays.xml定义数组。
colors.xml定义颜色和颜色字符串值。分别用Resources.getDrawable()和Resources.getColor()方法获取这些资源。
dimens.xml定义尺寸数据,可以用Resources.getDimension()方法取得这些资源。
strings.xml定义字符串值,可以用Resources.getString()或者Resources.getText()方法来获取这些资源。其中getText()方法将保留任何丰富的通常在UI中描述的文字样式。)
styles.xml定义样式对象。
4)res/menu/---菜单对象的XML文件,可以通过重载Activity类的onCreateOptionsMenu()方法,使用“MenuInflaterinflater = getMenuInflater(); inflater.inflate(R.menu.menu,menu; ”代码片段来获取。
5)res/anim/---XML文件,被编译成逐帧动画或者是补间动画的对象。
6)res/xml/---用来放置style、theme等xml文件的定义。也可放置任何XML文件,在运行时可通过Resources.getXML()方法读取。
7)res/raw/---存放任何被直接拷贝到设备上的文件。在程序被编译时,它们直接加到压缩文件中。在应用程序中可以通过用Resources.openRawResource(id)方法来获取资源,id的形式为:R.raw.filename。
所有资源都会被编译到最终的APK文件里。Android会自动创建一个类——R.java,这样你在代码中可以通过它关联到对应的资源文件。R.java中包含的子类的命名由资源的路径和文件的名称决定。
经验分享: 上述res/values/目录下的文件命名(arrays.xml、colors.xml等等)只是Android官方推荐的名称。实际上,无论文件如何命名,比如命名为my.xml,也都是可以的。 另外,如果项目工程较大,包含很多的模块,就可以考虑以模块化的方式配置资源文件。比如整个项目用到的字符串,可以根据模块划分,命名为“模块1简称_strings.xml”、“模块2简称_strings.xml”等等,这样方便以后的维护。 |
9.1.2 资源文件目录的修饰语
Android通过检测硬件和语言的设置,去加载特定的资源文件。用户可以在系统的设置中设置系统的语言。为了包含各种替代资源,需要在同一目录下创建并行的文件夹,并且在每个文件夹名字后面加上相应的指定,说明它使用的配置(如语言,屏幕的方向等等)。例如,下面是一个工程中包含一个字符串资源,一个是英文版的,一个是法文版的:
res/ ---->values-en/ --------------->strings.xml ----->values-fr/ --------------->strings.xml |
当Android检测到系统语言为英文的时候,就会自动去查找资源文件中最符合的资源来给代码调用,这里将会调用values-en/strings.xml,而当系统语言为法文的时候,则会调用values-fr/strings.xml。
既然存在多种替代资源,这里就会有Android加载资源文件的步骤的问题。所有的资源文件名都可以加上一个或多个修饰语,在调用资源的时候,系统通过检测硬件和语言的设置一层一层的筛选,找出最匹配的资源返回。Android系统支持很多种不同类型的修饰语,把修饰语加在资源文件夹名字的后面,通过破折号隔开。并可以将多个修饰语加在文件夹的后面,但是它们必须按照规定的顺序出现。例如,一个文件夹包含图像资源,并且完全指定出所有配置,如下所示:
res/drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480x320/ |
当然,你可以只指定一部分配置项,而将其他修饰语删除掉,只要将剩下的修饰语保留正确的顺序就可以。
下面我们按照顺序来对这些修饰语进行详细说明,请参考表9-1。
修饰语 | 值 |
语言 | 两个小写字母参照ISO639-1 标准。例如:en,fr,es |
地区 | 一个小写字母加两个大写字母,参照ISO3166-1-alpha-2 标准。 例如:rUS,rFR,rES |
屏幕方向 | Port(竖屏),land(横屏),square |
屏幕像素 | 92dpi,108dpi,等等 |
触摸屏类型 | Notouch(不支持触屏),stylus(手写笔),finger(手指触摸) |
键盘是否可用 | Keysexposed(可用),keyshidden(不可用) |
文本输入模式 | Nokeys(无键盘),qwerty(标准传统键盘),12key(12个键) |
导航模式 | Nonav,dpad,trackball,wheel |
屏幕分辨率 | 320×240,800×480,等等。大分辨率需要开始声明。 |
表9-1资源文件目录的修饰语
上面的这个列表,不包含设备的特殊参数,如载体、商标、设备/硬件、制造商。任何应用程序需要知道的设备信息,都在上面表格中的资源修饰语里。
下面是一些通用的关于资源目录的命名规范:
各个变量用破折号分开(每个基本的目录名后跟一个破折号)
变量是区分大小写的(所有变量名的大小写必须保持一致)
例如,一个drawable的目录必须命名为drawable-port,而不是drawable-PORT。
你不能有两个目录命名为drawable-port和drawable-PORT,即使故意将“port”和“PORT”指向不同的参数值。
同一类型的修饰语在一个文件名中只能出现一次(你不能这样命名drawable-rENrFR)
你可以指定多个参数来定义具体的配置,但参数必须保持上面表格中的顺序。例如,drawable-en-rUS-land指在US-English的设备上使用。
Android会试图找到最适合当前配置的目录,关于这一点将在下面详细讲述
表格里所有的参数的顺序是用来防止多个合格目录的事件发生(可看下面的例子)
所有的目录,无论是否合格,都是放在res/目录下的。合格的目录是不能嵌套的(你不能这样写res/drawable/drawable-en)
所有的资源将在代码或简单的资源引用语法,不加修饰的名字。因此,如果一个资源被命名为:
res/drawable-port-92dp/myimage.png
那么它可以这样被引用:
R.drawable.myimage(在代码中)
@drawable/myimage(在XML文件中)
9.1.3 程序加载资源文件的步骤
既然有如此繁多的资源文件,那么我们如何来对他们进行分类,并且做到国际化和本地化呢?这里,我们就必须先了解Android加载资源文件的步骤。
Android将挑选哪些资源文件在运行时会被使用,这取决于当前的配置。选择过程如下:
1)先找出包含匹配的资源的所有文件夹。
比如我们用到了资源myimage.png,而有下面的四个文件夹都包含这个资源。
res/drawable/myimage.png res/drawable-en/myimage.png res/drawable-port/myimage.png res/drawable-port-92dpi/myimage.png |
2)然后清除所有的不符合当前设备配置的资源。
例如,如果当前屏幕的像素是108dpi,那么将清除res/drawable-port-92dpi/。剩余下面的目录。
res/drawable/myimage.png res/drawable-en/myimage.png res/drawable-port/myimage.png |
当前语言是en-GB,屏幕方向是port,那么有两个符合配置的选项:res/drawable-en/和res/drawable-port/,目录res/drawable/将被清除,因为它和当前的配置有0个属性相同,而其他两个文件有一个和配置文件相同的属性。此时剩余下面的目录。
res/drawable-en/myimage.png res/drawable-port/myimage.png |
3)如果还有多个目录存在,则根据配置的优先级选取最终的资源文件。
上面表格的修饰语就是按照优先级来写的。也就是说,语言的优先级比屏幕方向的优先级高,所以在这个例子中,我们会通过语言环境来优先选择res/drawable-en/目录。最终只剩下下面的目录,系统会在此目录下选择文件。
res/drawable-en/myimage.png |
9.2国际化和本地化的支持
Internationalization(国际化)简称i18n,因为在i和n之间还有18个字符。Localization(本地化),简称L10n。国际化和本地化有一个通用的标准,一般的,在说明一个地区的语言时,用“语言_地区”的形式,比如zh_CN、zh_TW等等。
Android系统对i18n和L10n提供了非常好的支持。Android没有专门的API来提供国际化,而是通过对不同resource的命名来达到国际化的目的。同时,这种命名方法还可用于对硬件的区分,比如不同的新视屏,使用不同的图片资源。
下面的章节会详细的进行说明。
比如有两个string.xml的资源文件,分别在res\values目录下和res\values-zh目录下。
res\values\string.xml
<?xmlversion="1.0" encoding="utf-8"?> <resources> <stringname="hello">Hello, Test!</string> </resources> |
res\values-zh\string.xml
<?xmlversion="1.0" encoding="utf-8"?> <resources> <stringname="hello">你好,测试</string> </resources> |
在代码中调用:
Stringhello = getResources().getStringArray(R.string.hello); |
如果系统的语言是中文的情况下,hello的值是“你好,测试”;在其它情况下,hello的值是“Hello,Test!”。
从以上的示例可以看到,只要简单的通过对资源目录进行适当的命名,就可以实现应用程序的国际化。
经验分享: 需要国际化的内容,一般包括所有文字信息(包括带有文字的图片)。 一般的,在实际项目中,国际化的工作是另外一个团队完成的。所以,开发人员在开发过程中,务必不要在代码中直接硬编码可能需要国际化的内容,而是将这些内容统统都写到资源文件中,在代码中通过资源的ID进行引用。这样,国际化团队只需要对部分资源文件进行国际化即可。 国际化过程还有一个难点是,国际化以后,由于不同语言的字符长度串不同,UI布局可能有所改变,这就需要国际化以后仔细的进行测试。 |