10.6 图像(drawable)资源

源代码目录:src/ch10/Drawable

图像资源文件保存在res/drawable目录中。在图像资源目录中不仅可以存储各种格式(jpg、png、gif等)的图像文件,还可以使用各种XML格式的图像资源来控制图像的状态和行为。本节将详细介绍普通图像资源和各种XML格式的图像资源的使用方法。

10.6.1 普通图像资源

Android支持3种图像格式:png、jpg和gif。官方推荐使用png格式的图像资源(经常被用在透明或半透明效果中),jpg也可以考虑使用。但gif格式的图像文件并不鼓励使用(由于移动设备性能的限制,目前Android SDK并不支持动画gif,因此,根本没有必要使用gif格式的图像资源)。

假设有一个res/drawable/ball.png图像文件,在布局文件中可以使用如下代码引用这个图像资源。

<ImageView

  android:layout_height="wrap_content"

  android:layout_width="wrap_content"

  android:src="@drawable/ball" />

使用图像资源的Java代码如下:

Resources res = getResources();

Drawable drawable = res.getDrawable(R.drawable.ball);

10.6.2 XML图像资源

XML图像资源实际上就是在XML文件中指定drawable目录中的图像资源。除此之外,还可以额外指定图像的某些属性。例如,图像抖动、图像排列方式等。

XML图像资源使用<bitmap>标签定义。例如,下面的代码定义了一个XML图像资源。

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

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

  android:src="@drawable/icon"

  android:tileMode="repeat" />

<bitmap>标签经常会被使用在图层资源和图像状态资源中。这部分内容将在10.6.5小节和10.6.6小节中详细介绍。

10.6.3 Nine-Patch图像资源

Nine-Patch图像资源与普通图像资源类似,只是Nine-Patch图像资源文件必须以9.png作为文件扩展名,如abc.9.png、face.9.png。

Nine-Patch图像资源的主要作用如下:

防止图像的某一部分被拉伸。

确定将图像作为背景图的控件中的内容显示的位置。

图10-16是一个png图,在图像的上方有一个突出的尖角。当图像放大或缩小时,要保持这个尖角不变,就要将这个png图变成Nine-Patch格式的图像。

Android SDK本身提供了一个Draw 9-patch工具用来制作Nine-Patch格式的图像。读者可以运行<Android SDK安装目录>\tools\draw9patch.bat命令启动这个工具,界面如图10-17所示。

 

▲图10-16 带尖角的png图

 

▲图10-17 Draw 9-patch工具主界面

可以通过Draw 9-patch工具在png图的四周绘制1个像素粗的直线。上边缘和左边缘的直线分别表示图像在水平和垂直方向可拉伸的范围。如果在水平和垂直方向的某个区域不需要拉伸,可以不绘制相应的直线。如图10-17所示的图像上方的小尖角不需要拉伸,因此,小尖角上方未绘制直线。

Nine-Patch格式图像右边缘和下边缘的直线分别表示图像所在控件中内容的显示范围。内容只在右边缘和下边缘绘制直线的区域显示。表示内容显示范围和拉伸范围的两组直线有一个重要区别,就是表示内容显示范围的直线中间不能断开,而表示拉伸范围的直线中间可以断开,如图10-18所示。

 

▲图10-18 Nine-Patch格式图像正确和错误绘制演示

Nine-Patch图像资源与普通图像资源的使用方法相同。在引用时只写文件名,省略“.9.png”。如下面的例子所示。

假设有一个Nine-Patch格式的图像文件res/drawable/cloud.9.png。可以在布局文件中使用下面代码引用该图像。

<Button

  android:layout_height="wrap_content"

  android:layout_width="wrap_content"

  android:background="@drawable/cloud" />

通常很多控制图像局部拉伸的场景都会使用Nine-Patch格式的图像,例如,有两个TextView控件的背景图分别设置了Nine-Patch格式和普通格式的图像,图像上方都有一个小尖角,拉伸的效果将如图10-19所示。从显示效果可以很容易看出,未使用Nine-Patch格式图像的TextView控件的背景图上方的小尖角也被拉宽了,很不好看。

 

▲图10-19 Nine-Patch 格式与非Nine-Patch 格式图像的拉伸效果对比

10.6.4 XML Nine-Patch图像资源

Nine-Patch图像资源也有与其对应的XML图像资源,这一点与普通图像资源类似。只是使用<nine-patch>标签来引用Nine-Patch格式的图像,而且属性比<bitmap>标签少了很多,只有一个设置抖动的android:dither属性。下面是一个定义XML Nine-Patch图像资源的例子。

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

<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"

  android:src="@drawable/cloud"

  android:dither="false" />

10.6.5 图层(Layer)资源

图层资源有些类似于<FrameLayout>,所不同的是<FrameLayout>标签中可以包含任意的控件,而图层资源中的每一层只能包含图像。定义图层资源必须使用<layer-list>标签作为资源文件的根节点,<layer-list>标签中可以包含多个<item>标签,每一个<item>标签表示一个图像,最后一个<item>标签会显示在最顶层(这一点与<FrameLayout>标签相同)。下面的代码通过<item>指定了一个图像。

<item android:drawable="@drawable/image" />

默认情况下,图像会尽量充满显示图像的视图。因此,显示的图像可能会被拉伸。为了避免图像拉伸,可以在<item>标签中使用<bitmap>标签引用图像,代码如下:

<item>

  <!-- 图像在视图的中心显示 -->

  <bitmap android:src="@drawable/image"

     android:gravity="center" />

</item>

下面看一个完整的图层资源的例子。

源代码文件:src/ch10/Drawable/res/drawable/layer.xml

<!-- 定义图层资源,该资源由两个图像层叠放置 -->

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

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

  <item android:top="20dp" android:left="20dp">

    <bitmap android:src="@drawable/android1"

     android:gravity="center" />

  </item>

  <item android:top="20dp" android:left="20dp">

    <bitmap android:src="@drawable/android2"

     android:gravity="center" />

  </item>

</layer-list>

上面的代码涉及了<item>标签的几个控制偏移量的属性。这类属性一共有4个,它们的含义如下。

android:top:顶端偏移的像素。

android:left:左侧偏移的像素。

android:bottom:底端偏移的像素。

android:right:右侧偏移的像素。

下面在<ImageView>标签中使用这个图层资源。

源代码文件:src/ch10/Drawable/res/layout/layer.xml

<ImageView android:layout_width="fill_parent"

  android:layout_height="fill_parent"

  android:src="@drawable/layer" />

图层的显示效果如图10-20所示。

 

▲图10-20 图层的显示效果  

注意

虽然可以使用<FrameLayout>或其他的方法实现图10-20所示的效果,但图层无疑是最简单的方法。如无特殊需要,建议使用图层来实现多个图像重合的效果。

10.6.6 图像状态(State)资源

Android SDK提供的Button控件默认样式显得有些单调,而且这种样式与绚丽的界面搭配在一起极不协调。当然,我们可以使用ImageView控件配合不同状态的图像做出很酷的按钮,但这需要编写大量的Java代码。为此,Android提供了一种改变Button默认样式的方法,这种方法不需要编写一行Java代码。

当按钮处于不同状态(正常、按下、获得焦点等)时会显示不同的样式,这些样式一般使用不同的图像来渲染,这就需要指定与不同状态对应的图像,而图像状态资源就是用来指定这些图像的。

图像状态资源是XML格式的文件,必须以<selector>标签作为根节点。在<selector>标签中包含了若干个<item>标签,用来指定相应的图像资源。下面看一个修改Button样式的例子。

假设有3个图像:normal.png、focused.png和pressed.png,分别表示按钮默认的样式、获得焦点的样式,被按下的样式。在res/drawable目录中建立一个button.xml文件,并输入如下的内容。

源代码文件:src/ch10/Drawable/res/drawable/button.xml

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

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

  <!--按下状态 -->

  <item android:state_pressed="true"

      android:drawable="@drawable/pressed" />

  <!--获得焦点状态 -->

  <item android:state_focused="true"

      android:drawable="@drawable/focused" />

  <!--默认状态 -->

  <item android:drawable="@drawable/normal" />

</selector>

在<selector>标签中有3个<item>标签,其中前两个<item>标签分别将android:state_pressed和android:state_focused属性值设为true,表示当前<item>标签的android:drawable属性指定的图像是被按下和获得焦点的样式。

在布局文件中定义一个<Button>标签,并按下面的代码设置<button>标签的属性值。

源代码文件:src/ch10/Drawable/res/layout/button_state.xml

<Button android:layout_width="wrap_content"

  android:layout_height="wrap_content"

  android:background="@drawable/button"android:text="按钮" />

现在运行程序,会显示如图10-21所示的按钮默认样式。按下这个按钮(不要抬起来),会显示如图10-22所示的按钮按下后的样式。

 

▲图10-21 按钮的默认样式

 

▲图10-22 按钮按下后的样式

10.6.7 图像级别(Level)资源

图像状态资源只能定义有限的几种状态。如果需要更多的状态,就要使用图像级别资源。在该资源文件中可以定义任意多个图像级别。每个图像级别是一个整数区间,可以通过ImageView.setImageLevel或Drawable.setLevel方法切换不同状态的图像。

图像级别资源是XML格式的文件,必须将<level-list>标签作为XML的根节点。<level-list>标签中可以有任意多个<item>标签,每一个<item>标签表示一个级别区间。级别区间用android:minLevel和android:maxLevel属性设置。setImageLevel或setLevel方法设置的级别在某个区间内(android:minLevel <= level <= android:maxLevel),系统就会先用哪个区间对应的图像(用android:drawable属性设置)。

现在我们来做个实验。在res/drawable目录中放两个图像:lamp_on.png和lamp_off.png,然后在res/drawable目录中建立一个lamp.xml文件,并输入如下的内容。

源代码文件:src/ch10/Drawable/res/drawable/lamp.xml

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

<level-list xmlns:android="http://schemas.android.com/apk/res/android">

  <item android:drawable="@drawable/lamp_off" android:minLevel="6"

    android:maxLevel="10" />

  <item android:drawable="@drawable/lamp_on" android:minLevel="12"

    android:maxLevel="20" />

</level-list>

<level-list>标签中包含了两个<item>标签,分别指定了两个级别区间(6 <= level <= 10和12 <= level <= 20)。

在布局文件中定义一个<ImageView>标签,并按下面的代码设置<ImageView>标签的属性值。

源代码文件:src/ch10/Drawable/res/layout/level.xml

<ImageView android:id="@+id/imageview_lamp"

android:layout_width="fill_parent" android:layout_height="wrap_content"

android:src="@drawable/lamp" />

下面的代码在onCreate方法和两个按钮的单击事件方法中分别设置level的值为8、6和15。当level的值为6和8时,ImageView控件会显示lamp_off.png,当level的值为15时,ImageView控件会显示lamp_on.png。

源代码文件:src/ch10/Drawable/src/mobile/android/drawable/Level.java

public class LevelList extends Activity

{

  private ImageView ivLamp;

  @Override

  public void onCreate(Bundle savedInstanceState)

  {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

 

    ivLamp = (ImageView) findViewById(R.id.imageview_lamp);

    // 设置level为8,显示lamp_off.png

    ivLamp.setImageLevel(8);

  }

  // “开灯”按钮的单击事件方法

  public void onClick_LampOn(View view)

  {

    // 设置level为15,显示lamp_on.png

    ivLamp.setImageLevel(15);

  }

  // “关灯”按钮的单击事件方法

  public void onClick_LampOff(View view)

  {

    // 设置level为6,显示lamp_off.png

    ivLamp.getDrawable().setLevel(6);

  }

}

现在运行程序,单击“开灯”按钮,灯泡会变成开灯状态,如图10-23所示。单击“关灯”按钮,灯泡会变成关灯状态,如图10-24所示。

 

▲图10-23 开灯状态

 

▲图10-24 关灯状态  

注意

如果指定的level并没有在任何一个区间内,则系统会清空ImageView控件中的图像。

10.6.8 淡入淡出(Cross-fade)资源

如果想做出更绚的效果,那么就使用淡入淡出资源。在10.6.6小节和10.6.7小节介绍了切换不同图像状态的方法,但这些方法都是直接将图像进行简单的切换,并没有任何特效,这在一个对效果要求较高的程序中略显得有些单调。

淡入淡出资源同样也是切换两个图像(目前不支持多于两个图像的切换),并且使这两个图像以淡入淡出效果进行切换。如10.6.7小节中的开关电灯的例子,如果加上淡入淡出效果,电灯在开关时会逐渐变亮或逐渐变暗。下面在res/drawable目录中建立一个lamp.xml文件,并输入如下的内容。

源代码文件:src/ch10/Drawable/res/drawable/cross_fade.xml

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

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

  <item android:drawable="@drawable/lamp_off" />

  <item android:drawable="@drawable/lamp_on" />

</transition>

注意

<transition>标签中只能有两个<item>标签。

下面的<ImageView>标签使用了cross_fade.xml资源文件。

源代码文件:src/ch10/Drawable/res/layout/cross_fade.xml

<ImageView android:id="@+id/imageview_lamp"

  android:layout_width="fill_parent" android:layout_height="wrap_content"

  android:src="@drawable/cross_fade" />

从第1个图像(第1个<item>中指定的图像)切换到第2个图像要使用TransitionDrawable. startTransition方法,从第2个图像切换到第1个图像要使用TransitionDrawable.reverseTransition方法。下面的代码使用这两个方法让灯泡逐渐变亮或变暗。

源代码文件:src/ch10/Drawable/src/mobile/android/drawable/CrossFade.java

public class CrossFade extends Activity

{

  private ImageView ivLamp;

 

  @Override

  public void onCreate(Bundle savedInstanceState)

  {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    ivLamp = (ImageView) findViewById(R.id.imageview_lamp);

  }

  public void onClick_LampOn(View view)

  {

    TransitionDrawable drawable = (TransitionDrawable)ivLamp.getDrawable();

    // 从第1个图像切换到第2个图像。其中使用1秒(1000毫秒)时间完成淡入淡出效果

    drawable.startTransition(1000);

  }

  public void onClick_LampOff(View view)

  {

    TransitionDrawable drawable = (TransitionDrawable)iv Lamp.getDrawable();

    // 从第2个图像切换到第1个图像。其中使用1秒(1000毫秒)时间完成淡入淡出效果

    drawable.reverseTransition(1000);

  }

}

运行程序,单击“开灯”和“关灯”按钮,会看到电灯逐渐变亮或变暗,图10-25是淡入淡出的中间效果。

 

▲图10-25 淡入淡出的中间效果

10.6.9 嵌入(Inset)图像资源

如果显示的图像要求小于装载图像的视图(例如,背景图小于View区域),可以考虑使用嵌入图像资源。嵌入图像资源是XML格式的文件,只有一个<inset>标签。使用如下的4个属性设置图像距离上、下、左、右4个方向的距离。

android:insetTop:图像距离上边的距离。

android:insetRight:图像距离右侧的距离。

android:insetBottom:图像距离底边的距离。

android:insetLeft:图像距离左侧的距离。

下面的代码定义了一个嵌入图像资源。

源代码文件:src/ch10/Drawable/res/drawable/inset.xml

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

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

android:drawable="@drawable/background"

android:insetTop="50dp"

android:insetLeft="50dp"

android:insetBottom="50dp"android:insetRight="50dp" />

下面的布局代码只将<LinearLayout>标签的android:background属性值设为嵌入图像资源。

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

  android:orientation="vertical" android:layout_width="fill_parent"

  android:layout_height="fill_parent" android:background="@drawable/inset"/>

运行程序后,会发现背景图距离4个边的距离都是50dp,效果如图10-26所示。

 

▲图10-26 嵌入图像资源

10.6.10 剪切(Clip)图像资源

使用剪切图像资源可以只显示一部分图像,这种资源经常被用在进度条的制作上。剪切图像资源是一个XML格式文件,资源只包含一个<clip>标签。下面看一个制作进度条的例子。

首先准备两个png图像(background.png和progress.png),将它们放到res/drawable目录中,然后在res/drawable目录中建立一个clip.xml文件,并输入如下的内容。

源代码文件:src/ch10/Drawable/res/drawable/clip.xml

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

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

  android:drawable="@drawable/progress" android:clipOrientation="horizontal"

  android:gravity="left" />

<clip>标签使用了如下的3个属性来控制如何截取图像。

android:drawable:指定要剪切的原图像。

android:clipOrientation:截取的方向。可取的值:horizontal和vertical。分别表示水平和垂直方向截取图像。

android:gravity:表示如何截取图像。例如,left表示从左侧截取图像,right表示从右侧截取图像。

本例通过一个<LinearLayout>标签和一个<ImageView>标签实现进度条,布局代码如下:

源代码文件:src/ch10/Drawable/res/layout/clip.xml

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

android:orientation="vertical" android:layout_width="200dp"

android:layout_height="wrap_content" android:background="@drawable/background">

  <ImageView android:id="@+id/image" android:layout_width="fill_parent"

    android:layout_height="wrap_content" android:background="@drawable/clip"/>

</LinearLayout>

下面的代码截取了部分背景图像。

源代码文件:src/ch10/Drawable/src/mobile/android/drawable/Clip.java

ImageView imageview = (ImageView) findViewById(R.id.image);

ClipDrawable drawable = (ClipDrawable) imageview.getBackground();

drawable.setLevel(3000);

上面的代码涉及一个截取比例的问题。ClipDrawable类内部预设了一个最大的level值10000(Android SDK未提供API修改该值)。如果这个level的值为0,表示截取图像的宽度或高度为0,也就是说,图像就无法显示了。如果level的值为10000,表示显示全部的图像(不进行任何截取)。本例将level设为3000,表示从左侧截取30%的图像。显示效果如图10-27所示。

 

▲图10-27 进度条的效果

10.6.11 外形(Shape)资源

外形资源是一种非常有意思,也非常强大的资源。通过外形资源,可以为控件加上渐变背景色;使控件的四个角变成圆形;设置控件内容到控件边界的距离等。

外形资源使用<shape>标签中的子标签定义各种效果。例如,res/drawable/shape.xml文件中定义了渐变色、控件内容距离边界的距离、圆角和边框线。

源代码文件:src/ch10/Drawable/res/drawable/shape.xml

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

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

  android:shape="rectangle">

  <!-- 定义渐变色(从左下角到右上角绘制渐变色) -->

  <gradient android:startColor="#FFFF0000"

  android:endColor="#80FF00FF"android:angle="45" />

  <!-- 定义控件内容到边界的距离(到四条边界的距离都是7)  -->

  <padding android:left="7dp" android:top="7dp" android:right="7dp"android:bottom="7dp" />

  <!-- 定义边框线(边框线宽度是2,颜色为白色)  -->

  <stroke android:width="2dp" android:color="#FFF" />

  <!-- 定义圆角(圆角半径是8)  -->

  <corners android:radius="8dp" />

</shape>

定义外形资源时,需要使用<shape>标签的android:shape属性指定要绘制的形状。在本例中该属性值为rectangle,表示绘制矩形。android:shape属性可以指定如下4个值。

rectangle:矩形。

oval:椭圆。

line:直线。

ring:圆环。

下面的代码在<TextView>标签中使用了shape.xml资源文件。

源代码文件:src/ch10/Drawable/res/layout/shape.xml

<TextView android:background="@drawable/shape"

  android:layout_height="wrap_content" android:layout_width="wrap_content"

  android:layout_margin="20dp" android:text="Shape Label" />

也可以使用下面的代码设置TextView控件的背景色。

Resources res = getResources();

Drawable shape = res. getDrawable(R.drawable.shape);

TextView tv = (TextView)findViewByID(R.id.textview);

tv.setBackground(shape);

现在运行程序,会显示如图10-28所示的效果。

 

▲图10-28 TextView控件使用外形资源后的效果