8.3 应用资源的类型

从功能上来看,Android的资源文件可以划分成不同的类型,每种类型的资源文件都有其特有的格式、特征以及不同的用途。

本节将逐一介绍不同类型资源文件的特征和使用方式。

8.3.1 界面和样式

界面资源文件,指的是放在layout目录下的XML资源文件,用来描述界面的控件信息。第7章介绍过Android的控件的使用,在实际开发中,通常是通过界面资源文件来描述界面控件样式,而不是通过代码进行构造。

一个简单的界面示例如下,在该界面中,包含了上下并列的两个按钮:


<!—文件名是main.xml—>

<?xml version="1.0"encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<Button android:id="@+id/confirm"

android:text="确定"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#8b8b8b"/>

<Button android:id="@+id/cancel"

android:text="取消"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#8b8b8b"/>

</LinearLayout>


从示例中可以看到,每个控件对象都对应着一个XML元素(XML element),该元素内的XML属性(XML attribute),用于描述控件对象的样式和信息,与调用该控件对象的相关函数对应。比如,设置Button的android:text属性值,等同于调用Button.setText函数。

通过XML的层级关系,可以完美地表达控件之间的父子关系,比如在此例中,两个按钮控件对象都是线性容器控件的子控件。

在代码中,可以通过R类来设定界面控件,调用Activity.setContentView函数可将R类中的界面描述设置到界面组件对象中:


setContentView(R.layout.main);


而使用android.view.LayoutInflater类,则可以将任意一个界面资源实例化成一个控件对象:


LayoutInflater inflater=LayoutInflater.from(this);

View main=inflater.inflate(R.layout.main, null);


通过资源文件,可以将界面逻辑与功能逻辑有效地分割开来,使得界面可以独立于逻辑进行变化。并且,Android为编辑界面资源文件提供了可视化工具,利用ADT插件,可以在Eclipse中所见即所得地进行编辑。

与写代码一样,界面资源文件的编写也需要遵循DRY(Don't Repeat Yourself)原则,不断地进行重构,保证资源文件的可维护性,使其能够更快地适应变化。

为了提升界面资源文件的复用度,界面资源文件支持通过<include>属性进行嵌套,比如,如果需要在某个界面的下面增加两个按钮,可以不必额外编写,而是直接通过<include>嵌入上例中的main.xml文件:


<?xml version="1.0"encoding="utf-8"?>

<LinearLayout…>

<include layout="@layout/main"/>

</LinearLayout>


基于文件的复用粒度较粗,场景也比较局限,在上例中,哪怕需要的按钮文字略有区别都不能进行复用。而在实际开发中,很多控件对象的属性值都相同,比如,设计者可能会为所有的按钮设置同样的背景色和尺寸。如果为所有按钮都赋值一次,一旦进行调整,就需要逐一修改,不满足DRY的原则。

为此,Android提供了样式资源,通过它可以有效地解决上述问题。每个样式资源,其实就是一组控件属性的集合,所有的样式都在values/styles.xml中进行定义。比如,将按钮的共同属性抽取成样式,可以如下定义:


<?xml version="1.0"encoding="utf-8"?>

<resources>

<style name="MyButton">

<item name="android:layout_width">wrap_content</item>

<item name="android:layout_height">wrap_content</item>

<item name="android:textColor">#000000</item>

</style>

</resources>

通过该样式信息,可以如此重构前例中的main.xml文件:

<?xml version="1.0"encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<Button android:id="@+id/confirm"

android:text="确定"

style="@style/MyButton"/>

<Button android:id="@+id/cancel"

android:text="取消"

style="@style/MyButton"/>

</LinearLayout>


在Android中,样式是可以通过“继承”进行复用的,比如,要在普通按钮样式的基础上派生一个字形为粗体、长度为整屏样式的按钮,可以如此定义:


<style name="MyButton.Blod_Long"parent="MyButton">

<item name="android:layout_height">fill_parent</item>

<item name="android:textStyle">bold</item>

</style>


其中,MyButton.Blod_Long样式继承了MyButton样式的所有属性,是MyButton的子样式。MyButton.Blod_Long中定义的android:height属性,与MyButton中定义的发生了冲突,在这种情况下,对应父样式的属性值会被忽略,犹如面向对象的“重载”。

通过parent属性可以明确地定义继承关系,但在此例中,parent属性可以省略,因为样式名的“”就蕴含了继承的概念,MyButton.Blod_Long会默认派生自MyButton。只有在命名规则不满足默认继承的条件下,才需要使用parent属性显性地指明继承对象。

尽可能地使用界面和样式资源来构建交互界面,是非常好的编程实践。因此,当开发中需要定制自定义控件时,也应该把控件中可配置的内容用资源文件来描述。这时候,就需要使用属性资源attrs.xml来定制控件中可使用资源文件配置的属性信息。

比如,在Android原生的邮件应用中,有一个自定义控件SizeBoundingFrameLayout,它派生自FrameLayout容器控件,对比其父类,多了一个最大高度和最大宽度的约束。为了能够在资源文件中方便的定义控件的最大高度和最大宽度,邮件应用在其属性资源中,为控件定义了最大高度maxHeight和最大宽度maxWidth的属性:


文件attrs.xml,定义在values目录下

<?xml version="1.0"encoding="utf-8"?>

<resources>

<declare-styleable name="SizeBoundingFrameLayout_attributes">

<attr name="maxWidth"format="dimension"/>

<attr name="maxHeight"format="dimension"/>

</declare-styleable>

</resources>


每个属性项,都有名字和格式信息,在上例中,最大宽度的属性名字是maxWidth,其值的类型是dimension,表示长度。当在界面资源中使用SizeBoundingFrameLayout控件时,就可以使用该属性来设置最大宽度和最大高度:


<?xml version="1.0"encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:ex="http://schemas.android.com/apk/res/com.android.email"

…>

<com.android.email.SizeBoundingFrameLayout

ex:maxWidth="200dip"

ex:maxHeight="800dip"

…/>

</LinearLayout>


在使用时,需要引入自定义属性的名字空间,如上例中的xmlns:ex,然后在此名字空间内容使用该属性,ex:maxWidth、ex:maxHeight等。