1.1 外部资源
不论你是什么开发环境,对于开发来说最好的做法就是不要将资源(例如图片等)路径这些整合到代码中。Android支持将这些资源外部化,范围包括一些简单的值,如字符串、颜色和更加复杂的图片资源、主题和动画。也许最强大的外部资源是布局。
通过外部资源化,可以让维护、升级和管理变得更加容易,这会让你更加容易定义不同的资源值去支持不同的硬件和国际化。
接下来你会了解到Andriod是如何为不同的硬件、语言以及位置动态的选择资源。这就需要你去为不同的语言、国家、屏幕以及键盘创建不同的资源。当应用启动后,Android会自动选择资源而不用编写任何代码。
这样你就可以基于屏幕大小更改布局,并且可以定制基于语言和国家定制提示信息。
1.1.1 创建资源
应用的资源都被存储在项目的 res/ 文件夹目录下。在这个文件夹中每种可用的资源类型都被存储在子文件夹下。
如果你使用ADT插件向导创建一个项目,它会为你创建一个res文件夹,它下边的子文件夹包括values,drawable- ldpi,drawable-mdpi,drawable-hdpi,和一个默认的layout布局资源,应用图片和分别定义的string 资源。如图3-4所示。
注意那三个Drawable资源文件夹创建了三种不同的图标,分别是低、中、高 DPI显示方式。
九个主要的资源类型有不同的文件夹:simple values,Drawables,layouts,animations,styles,menus,searchables,XML和raw resources。当你的应用被打包的时候,这些资源就会尽可能高效的被包含到你应用的package中。
这个过程也会产生一个R class文件,这个文件引用了项目中的所有资源。这个为你的代码提供了资源的引用以及设计时候的变量检查。
接下来的部分描述了这些目录中可用的资源类型,以及如何为你的应用创建它们。
资源文件的命名要遵循以下方式:仅使用小写字母、数字、.和下划线_
图3-4
Creaing Simple Values
支持的简单值包括strings,colors,dimensions,和string或者integer数组。所有的简单值被存储在res/values文件夹的下的XML文件中。
每个XML文件中指定了存储的标签的类型值。如图Listing 3-1显示的XML文件
图 Listing 3-1
上边这个例子中包含了所有的简单值类型。按照约定俗成的方式来说,每种类型的值都会被分成不同的文件存储。例如,res/values/strings.xml文件只是包含string的资源类型定义。
接下来的我们会详细介绍定义simple resource的配置项。
Strings
外部化你的strings帮助你更好的维护你的应用,并且能让国际化更加简单。
String 资源被通过<string>标签引用。如下XML片段所示:
<string name="stop_message">Stop.</string>
Android支持简单的文本样式,所以你可以使用HTML标签<b>,<i>和<u>对你的文本strings去进行加粗,斜体、或者增加下划线,如下XML片段所示:
<string name="stop_message"><b>Stop.</b></string>
对于String.format方法,你可以使用资源strings作为输入参数。但是,String.format方法不支持上边描述的文本样式。为了将格式化的文本能使用String.format方法,当你创建资源时你不得不去讲HTML标签转换,如下所示:
<string name="stop_message"><b>Stop</b>. %1$s</string>
在你的代码中,使用Html.fromHTML方法去讲这个转换成一个有样式的字符序列。
String rString = getString(R.string.stop_message);
String fString = String.format(rString, "Collaborate and listen.");
CharSequence styledString = Html.fromHtml(fString);
Colors
使用<color>标签去定义一个新的color资源。通过使用#符号去指定一个color的值,然后就是将红、绿、蓝值进行组合来描述具体的颜色:
· #RGB
· #RRGGBB
· #ARGB
· #AARRGGBB
下边的例子展示了如何去指定一个蓝色和渐变绿色的值:
<color name="opaque_blue">#00F</color>
<color name="transparent_green">#7700FF00</color>
Dimensions
Dimensions(尺寸)是最常见的被引用的资源,一般他们在创建布局的时候用来指定边框的像素和字体高度。
我们使用<dimen>标签去声明一个dimension资源,指定dimension尺寸值,同时指明一个尺寸的单位值:
· px (screen pixels)
· in (physical inches)
· pt (physical points)
· mm (physical millimeters)
· dp (density-independent pixels relative to a 160-dpi screen)
· sp (scale-independent pixels)
这些可选项允许你定义不同的尺寸,而不是定义成绝对的大小,我们针对不同的硬件和屏幕可以进行量体裁衣了。
下边的XML片段展示了如何指定一个大的字体和一个标准边框的尺寸值
<dimen name="standard_border">5dp</dimen>
<dimen name="large_font_size">16sp</dimen>
Styles and Themes
Style资源通过你为Views指定的属性值保持应用的界面和感受。最常见使用themes和styles的方式就是存储颜色和字体。
你可以简单的更改你应用的界面,只需要在你项目manifest中简单的指定一个不同的style和theme。
通过一个包含name属性的<style>标签去创建一个style,同时他的内部可以包含一个或者多个<item>标签。每个item标签包含一个name属性用来指定被定义的属性。标签本身应该包含值,如下代码所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="StyleName">
<item name="attributeName">value</item>
</style>
</resources>
Styles支持集成的方式,这个可以通过<style>标签的parent属性指定。这让他更加容易的创建简单的变量。
下边的例子展示了两个styles能被一个theme使用:一个基本的style设置了几个文本属性,另外一个style修改了那个基本的style,并使其定义的字体变小。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseText">
<item name="android:textSize">14sp</item>
<item name="android:textColor">#111</item>
</style>
<style name="SmallText" parent="BaseText">
<item name="android:textSize">8sp</item>
</style>
</resources>
Drawables
Drawable 资源包括bitmaps和NinePatch(PNG)图片。他也包含一些更加复杂的组合Drawables,例如LevelListDrawables和StateListDrawables,这些能在XML文件中定义。
所有的Drawables被存在独立的文件夹res/drawable中,对于一个Drawable资源的标识是一个没有拓展的小写文件名。
注意:最好的bitmap资源是PNG,虽然GPG和GIF文件也会被支持。
Layout
Layout资源让你的表现层松耦合,我们可以在XML中定义你的布局而不是在代码中定义。
最常见layout用法是为一个Activity定义一个用户接口。一旦在XML中定义了,布局会在Activity中通过SetContentView
使用,通常这个被包含在onCreate方法中。你也可以从其他的layout资源中引用layouts,例如为每行引用一个List View
layout。
使用layout去创建你的展示界面在android中是一个最佳实践。代码与layout松耦合能让你为不同的硬件配置创建最优的
layout,例如不同的屏幕大小、表现方式、键盘和触摸屏等等。
每个布局的定义被存储在一个单独的文件中,包含了一个layout,存放在res/layout 文件夹中。文件名会变成这个资源
的唯一标识。
针对layout容器和View的完整解释会在下个章节阐述,图Listing 3-2展示了使用项目向导创建的布局信息。他使用了一
个线性布局作为布局容器,并且包含了一个文本视图展示了“Hello World”的问候信息。
图 Listing 3-2
Animations
Android支持两种类型的动画。Tweened 动画可以被用来旋转,移动、拉伸和隐藏;或者你可以创建帧动画去展示一系列的Drawable图片。深度使用创建和应用动画将会在第15张详细说明。
把动画定义为外部资源可以让你在多个地方重用相同系列动画,并且给你提供了一个基于不同设备和定位去显示不同的动画。
Tweened Animations
每个tweened动画都被以单独文件形式存储在项目的res/anmi文件夹下。就像其他布局和Drawable资源,动画文件的名字会被用作资源的唯一标识。
动画可以被定义为渐变、所见、移动或者旋转。
表 Table 3-1 展示了每种动画类型支持的有效属性,属性值
你可以使用set标签创建一个组合动画。一个动画集合包含一个或者多个动画转换,同时支持多个其他标签和属性供我们设定动画如何去运行。
下边列表展示了一些可用的标签集合。
duration:动画运行时间
startOffset:动画开始前延迟播放时间
fillBefore:设定为true的时,在动画开始前使用转换
fillAfter:设定为true时,在动画播放完毕后使用转换
Interpolator:设定动画播放速度。第十五章的时候我们将深入探讨。需要说明的是,使用系统动画资源要通过android:anim/interpolatorName
表 table 3-1:动画类型属性:
注意:如果你设定startOffset标签,一个集合中所有的动画效果都会一起执行。
下边的例子展示了一个动画设定,当他缩小或者渐变退出的时候旋转360度。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="500"
android:duration="1000" />
<scale
android:fromXScale="1.0"
android:toXScale="0.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="500"
android:duration="500" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:startOffset="500"
android:duration="500" />
</set>
Frame-by-Frame Animations
帧动画允许你创建一系列的Drawables,在View的背景中每个都会展示一段时间。
因为帧动画展现的是存储在res/drawable文件中的动画Drawables,而不是tweened 动画,使用它们的文件名作为资源ID。
下边的XML片段展示了一个简单的循环显示一系列的bitmap资源动画,并且每个展示0.5秒。为了使用这个片段,你需要创建新的图片资源rocket1到rocket3.
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/rocket1" android:duration="500" />
<item android:drawable="@drawable/rocket2" android:duration="500" />
<item android:drawable="@drawable/rocket3" android:duration="500" />
</animation-list>
Menus
创建menu(菜单)资源会进一步分离你的展现层和代码。这样你在设计菜单的布局的时候可以使用XML而不是直接在代码中构造它们。
Menu资源能被用来定义你应用中的Activity和context menus,同时你也可以在代码中构造menu项。一旦你在XML定义了,通过MenuInflator的inflate方法menu会被展示到你的应用中,这个一般都在onCreateOptionsMenu方法中调用。在第四章中你可以看到menus的深入探讨。
每个menu的定义都在res/menu文件中以独立文件的方式存储,每个里边都包含一个menu。文件名会作为资源的标识。使用XML去定义你的menus在Android中是一个最佳实践。
一个全方位的阐述menu配置项的说明在下章中被说明,但是 图Listing 3-3 展示一个简单的menu例子。
图 Listing 3-3
1.1.2 使用资源
当你创建完资源的同时,Android提供了一些系统资源,这些你都能在应用中使用。这些资源可以被直接在应用中使用,也可以在其他资源中北引用。
接下来你将会了解到如何为不同的语言、地区以及硬件定义不同的资源值。有一点你需要知道,使用资源的时候你不能指定某个特定的版本。Android会基于当前硬件和设备的设置去自动的为给定的资源id选择最合适的值。
在代码中使用资源
在代码中访问资源可以使用静态类R class。R是一个基于你的外部资源的衍生类,当你项目编译的时候被创建。R class针对你已经定义的每种类型的资源对应包含一个静态子类。例如,新创建的项目默认包含R.string 和R.drawable子类。
注意:如果你在eclipse中使用ADT插件,当你做任何外部资源的更改时,R class将会自动的被创建。如果你没有使用插件,只是使用AAPT工具去编译你的项目并产生R class。R是一个编译产生的类,所以不要做任何手工的修改,否则当资源文件修改后,他们就会丢失。
每个R class暴露的子类资源都被认为是变量,变量名和资源标识一一对应。例如:R.string.app_name 或者R.drawable.icon
这些变量的值是对资源表中的资源位置的一个引用,不是资源实例的本身。
在一些构造函数或者方法中,例如setContentView,接收一些资源标识,你可以在资源变量中传递,如下代码所示:
// Inflate a layout resource.
setContentView(R.layout.main);
// Display a transient dialog box that displays the
// error message string resource.
Toast.makeText(this, R.string.app_error, Toast.LENGTH_LONG).show();
当你需要资源实例本身,你需要使用helper方法到资源表中抽取他们。在你的应用中,资源表被作为一个Resources class展现。
因为这些方法的展现基于应用的资源表,这些帮助方法不能是静态的。在你的应用上下文使用getResources方法去访问你应用的资源实例。代码如下:
Resources myResources = getResources();
Resources 类包含每个可用资源类型的的getter方法,并且你可以通过传递resource ID获取。下边代码展示了用helper方法去返回一个指定的资源值。
Resources myResources = getResources();
CharSequence styledText = myResources.getText(R.string.stop_message);
Drawable icon = myResources.getDrawable(R.drawable.app_icon);
int opaqueBlue = myResources.getColor(R.color.opaque_blue);
float borderWidth = myResources.getDimension(R.dimen.standard_border);
Animation tranOut;
tranOut = AnimationUtils.loadAnimation(this, R.anim.spin_shrink_fade);
String[] stringArray;
stringArray = myResources.getStringArray(R.array.string_array);
int[] intArray = myResources.getIntArray(R.array.integer_array);
帧动画资源被设定为AnimationResources。你可以使用getDrawable获取并且对返回值进行转换。如下代码所示:
AnimationDrawable rocket;
rocket = (AnimationDrawable)myResources.getDrawable(R.drawable.frame_by_frame);
在资源中内部引用资源
你也可以将资源引用作为属性值放到其他的XML资源中
这个针对layouts布局和styles风格样式十分有用,允许你针对主题和本地string以及图片创建特定的变量。他也是用来解决不同的屏幕大小和分辨率的最有效的方法。
引用其他package资源文件中的资源需要使用@标识,如下代码所示。
attribute="@[packagename:]resourcetype/resourceidentifier"
备注:Android会假定你正使用同package中的资源,如果你需要使用其他package中的资源,你仅仅需要设定全路径名称。图Listing3-4 展示了一个使用color,dimension和string资源的布局器。
图Listing 3-4
使用系统资源
本地原生的Android应用有很多外部化资源,为你提供了strings,images,动画,styles和layouts在你的应用中使用。
在代码中访问系统的资源与访问你自己的资源类似。不同的是,你使用原生的Android资源classes是使用android.R类,而不是应用的引用类R。下边的代码展示了应用上下文中使用getString方法从系统资源中获取一个错误信息提示。
CharSequence httpError = getString(android.R.string.httpErrorBadUrl);
在XML文件中访问系统资源需要指定Android作为package的名字。如下XML片段所示:
<EditText
android:id="@+id/myEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@android:string/httpErrorBadUrl"
android:textColor="@android:color/darker_gray"
/>
在当前theme中使用style
使用themes是一个较好的方法去保证应用UI的流畅。相对于完全定义每一个style,Android提供了一个快捷方式让你去使用当前应用的theme的styles。
要这样使用需要使用 ?android:而不是@ 标识作为你想使用资源的前缀。下边的例子展示了一个使用当前theme的文本颜色而不是一个外部资源的文本颜色。
<EditText
android:id="@+id/myEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/stop_message"
android:textColor="?android:textColor"
/>
这个技术让你创建的styles会随着当前theme的改变而变化,不需要你去修改每个style resource。
1.1.3 资源例子作业
1.1.4 为不同的语言和硬件创建资源
外部化资源的一个最令人信服的理由是Android的动态资源选择机制。
使用下边描述的目录结构,你可以为不同的语言,位置和硬件配置创建不同的资源值。Android将会在运行时动态选择这些值。
你可以在res文件下通过使用平行的目录结构选择不同的资源值。
一个(-)连字符被用来区分你提供指定的相关状态信息。
下边文件夹中的目录结构展示了默认的string值,法语和加拿大法语的变量值的存放方式:
Project/
res/
values/
strings.xml
values-fr/
strings.xml
values-fr-rCA/
strings.xml
下边的列表提供了你可以使用定制的相关资源值
Mobile Country Code and Mobile Network Code (MCC/MNC):国家和当前设备中的SIM卡的可选择网络。MCC可以通过mcc 加上三个国家代码指定。MNC可以通过mcc加上两个或者三个国家码指定(例如:mcc234-mnc20 or mcc310)。你可以到如下的网站找到MCC/MNC的编码:http://en.wikipedia.org/wiki/Mobile_Network_Code
· Language and Region:语言可以通过ISO639-1 标准的两个小写字母,配合一个小写的r加上ISO3166-1-ALPHA-2语言编码
· 指定地区(例如:en,en-rUS,或者en-rGB).
· Screen Size:可以选择small(比HVGA还小),medium(大于HVGA小于VGA),或者large(比VGA大)。
· Screen Width/Length:特意为宽屏资源设计指定long或者notlong(例如:WVGA 是long,QVGA是notlong)。
· Screen Width/Length:可以选择port(portrait),land(landscape),或者square(square)
· Screen Pixel Density:以点计算每英寸的像素比重(dpi)。最好的方法是使用ldpi,mdpi或者hdpi去指定low(120dpi),medium(160dpi),或者high(240dpi)。你也可以针对位图资源指定nodpi,这样是在你不想指定支持的准确像素。不像其他的资源类型,Android不需要针对资源的一个精准匹配。当选择合适的文件夹,他就会自动的选择最近的去匹配设备的像素和自动的拓展Drawables的结果。
· Touchscreen Type:可以选择 notouch,stylus,finger
· Keyboard Availability:可以选择 keysexposed keyshidden,keyssoft
· Keyboard Input Type:可以选择 nokeys,qwerty,12key
· UI Navigation Type:可以选择nonav,dpad,trackball,whell
你可以为任何资源类型指定多个值,通过一个连接符(-)进行分割。任何组合都是可以被支持的,但是需要有一个先后
顺序,一次只能有一个被使用。
下边展示了针对不同的Drawable资源,有效和无效的目录名
· Valid:
drawable-en-rUS
drawable-en-keyshidden
drawable-long-land-notouch-nokeys
· Invalid:
drawable-rUS-en (out of order)
drawable-rUS-rUK (multiple values for a single qualifier)
当Android在运行时获取资源的时候,他会找到当前可用方法中最匹配的那个。当有一大串的可选资源存在的时候,他会选择一个最匹配的。如果两个资源的匹配度相同,他会基于匹配的顺序选择。
注意:如果给定的设备上没选中的资源,你的应用访问资源的时候会抛出异常。为了避免这个,最好的方式就是永远为每个资源指定默认值。
1.1.5 运行时配置变更
Android是可以通过终止和重启每个应用去加载资源值在运行时更改语言、位置和硬件配置。
默认设定的行为模式不会总是最方便和最想要的,特别当一些配置变化的时候(例如屏幕分辨率和键盘是否可用)都会发生,就像用户旋转设备或者推出键盘。你需要定制你的应用去感应、检测这些改变,并且做出反馈。
为了侦听一个Activity在运行时配置的改变,我们可以通过增加一个android:configChange属性到他们的manifest节点,指定配置改变时你想做的处理。
下边列表描述了配置改变时候你能做的指定:
· orientation:当屏幕在横竖做旋转的时候
· keyboardHidden:键盘被展示或者隐藏
· fontScale:用户改变了字体大小
· locale:用户改变了语言设置
· keyboard:键盘类型改变了。例如,电话可以有一个12键的快捷键,也可以变成全键盘
· touchscreen or navigation:键盘类型或者导航方式发生变化。这些事情都不会正常发生。
一定情况下,多个事件会同时被触发。例如,当用户推开键盘,很多设备会触发keyboardHidden和orientation 事件。
你可以选择多个你想处理的时间通过(|)符号进行分割。
图Listing 3-5 展示了一个activity代码声明了他要处理的一些动作,当屏幕旋转或者键盘可见的时候被触发。
图 Listing 3-5
增加一个android:configChanges属性禁止特定的配置在重启后变更,而不是在Activity中触发onConfigurationChanged方法。通过覆盖这个方法去处理配置的改变,通过传进来的Configuration对象去确认新的配置值,如图Listing 3-6中显示的那样。确保回调父类的方法重新加载Activity的资源值,以免他们改变。
图Listing 3-6
当onConfigurattionChanged被调用,Activity资源变量值已经更新为最新值,所以他们可以被安全的使用。
任何你在应用中没声明的配置改变都会导致你的Activity去重启,而不是去调用onConfigurationChanged方法。