13.4 图像控件(ImageView)

前面介绍过很多可以显示图像的控件,但显示图像对于这些控件来说都只是辅助功能,而本节要介绍的ImageView控件则是专门用来显示和控制图像的,例如,放大、缩小、旋转图像。

13.4.1 ImageView控件的基本用法

源代码目录:src/ch13/ImageView

ImageView控件可用于显示Android系统支持的图像,其支持的图像格式有gif、jpg、png、bmp等。在布局文件中使用<ImageView>标签来定义一个ImageView控件,代码如下:

源代码文件:src/ch13/ImageView/res/layout/main.xml

<ImageView android:id="@+id/imageview" android:layout_width="wrap_content"

  android:background="#F00" android:layout_height="wrap_content"

  android:src="@drawable/icon" android:scaleType="center" />

在上面的代码中通过android:src属性指定了一个图像资源ID,并使用android:scaleType属性指定ImageView控件显示图像的方式。例如,center表示图像显示在ImageView控件的中心。如果将android:scaleType属性设为fitCenter,表示将图像按比例缩放至合适的大小,并显示在ImageView控件的中心。通常在设计相框时将android:scaleType属性设为fitCenter,这样可以使照片按比例显示在相框的中心。

源代码文件:src/ch13/ImageView/res/layout/main.xml

<ImageView android:layout_width="300dp" android:layout_height="200dp"

  android:background="#FFF" android:src="@drawable/background"

  android:scaleType="fitCenter" android:padding="10dp" />

上面的代码直接设置了ImageView控件的宽度和高度,也可以在代码中设置和获得ImageView控件的宽度和高度。

源代码文件:src/ch13/ImageView/src/mobile/android/imageview/Main.java

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

// 设置ImageView控件的宽度和高度

imageView.setLayoutParams(new LinearLayout.LayoutParams(200, 100));

// 获得ImageView控件的宽度和高度,并将获得的值显示在窗口的标题栏上

setTitle("height:" + imageView.getLayoutParams().width + " height:" + imageView.get LayoutParams().height);

运行本例,显示的效果如图13-19所示。

 

▲图13-19 图像的显示方式

13.4.2 显示指定区域的图像

源代码目录:src/ch13/RectImageview

虽然ImageView控件可以用不同的缩放类型(通过scaleType属性设置)显示图像,但遗憾的是ImageView控件只能显示整个图像。如果只想显示图像的某一部分,单纯使用ImageView控件就无能为力了。

尽管ImageView控件无法实现这个功能,但可以采用“曲线救国”的方法来达到只显示图像的某一部分的目的。该方法的基本原理是首先获得原图像的Bitmap对象,然后使用Bitmap.createBitmap方法将要显示的图像区域生成新的Bitmap对象,最后将这个新的Bitmap对象显示在ImageView控件上。

本例使用了一个分辨率为500408的图像文件(dog.jpg),当触摸该图像的某一点时,会将以该点为左上顶点的一个正方形区域复制到另一个100dp100dp的ImageView控件中。下面的代码定义了两个ImageView控件,分别用于显示原图像和原图某个区域的图像。

源代码文件:src/ch13/RectImageview/res/layout/main.xml

<!-- 显示原图像 -->

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

  android:background="#F00" android:layout_height="261dp" android:src="@drawable/

  background"  android:scaleType="fitStart" />

<!-- 显示原图像的指定区域 -->

<ImageView android:id="@+id/imageview2" android:layout_width="100dp"

  android:background="#F00" android:layout_height="100dp"

  android:layout_marginTop="10dp" android:scaleType="fitCenter" />

第1个<ImageView>标签的android:scaleType属性值为fitStart,表示让图像从左上角开始显示(左上角对齐)。

本例的核心代码在Activity.onTouch方法中,要捕捉onTouch事件,必须实现OnTouchListener接口。实现单击图像截取图像区域的完整代码如下:

public class Main extends Activity implements OnTouchListener

{ 

  private ImageView imageView1;

  private ImageView imageView2;

  @Override

  public boolean onTouch(View view, MotionEvent event)

  {

    try

    {

      BitmapDrawable bitmapDrawable = (BitmapDrawable) imageView1

.getDrawable();

      // 由于在ImageView控件中显示的图像尺寸会小于原图像尺寸(ImageView控件的高度是261,

      // 而图像的高度是408,所以需要计算一个ImageView控件中的坐标与图像实际坐标的换算比例

      // 也就是用原图像高度除以ImageView控件的高度

      float scale =

(float)bitmapDrawable.getBitmap().getHeight()/imageView1.getMeasuredHeight();

      // 将当前单击的横坐标换算成原图像的横坐标

      int x = (int) (event.getX() * scale);

      // 将当前单击的纵坐标换算成原图像的纵坐标

      int y = (int) (event.getY() * scale);

      // 将要截取的宽度换算成原图像对应的宽度

      int width = (int)(100 * scale);

      // 将要截取的高度换算成原图像对应的高度

      int height = (int)(100 * scale);

      // 从原图像截取图像区域,并将截取后的图像显示在第2个ImageView控件中

      imageView2.setImageBitmap(Bitmap.createBitmap(

          bitmapDrawable.getBitmap(), x, y, width, height));

 

    }

    catch (Exception e)

    {

      Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();

    }

    return false;

  }

  @Override

  public void onCreate(Bundle savedInstanceState)

  {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    imageView1 = (ImageView) findViewById(R.id.imageview1);

    imageView2 = (ImageView) findViewById(R.id.imageview2);

    imageView1.setOnTouchListener(this);

  }

}

运行本例后,单击第1个ImageView控件中的某一点,将显示如图13-20所示的效果。

 

▲图13-20 截取图像的指定区域

扩展学习:如何装载大图像

可以通过android.graphics.BitmapFactory类的静态方法从不同来源创建Bitmap对象。例如,下面的代码从SD卡中的图像文件创建了一个Bitmap对象。

Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/abc.jpg");

虽然从SD卡、资源中装载图像,并创建Bitmap对象很容易,但这里有一个问题,手机毕竟不是PC,内存和其他硬件资源有限(至少在可预见的未来手机还无法和同一时代的PC相比),无法一次性装载过大的图像(例如,装载文件大小在1MB以上的图像很可能会出现内存溢出错误)。那么在这种情况下,就需要将图像按比例缩小来装载(不是显示效果上的缩小,而是图像本身的缩小,这一点和ImageView控件按比例缩小图像不同),这就需要使用decodeFile方法的第2个参数(其他类似的方法,例如,decodeResource方法也有这个参数)来设置缩小的比例。

decodeFile方法的第2个参数的数据类型是android.graphics.BitmapFactory.Options,Options类有一个inSampleSize字段用于设置图像缩小的比例。该属性是int类型,例如,如果该属性的值是5,则以1/5,也就是20%的大小来缩小原图像。下面的代码将以25%的大小生成一个缩小版的Bitmap对象。

Options options = new Options();

options.inSampleSize = 4;

Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/abc.jpg", options);

在创建Bitmap对象后,就可以直接使用ImageView.setImageBitmap方法将缩小版的图像在ImageView控件上显示了。

13.4.3 缩放和旋转图像

源代码目录:src/ch13/ResizeRoundImage

缩放图像的方法很多,最简单的方法无疑是改变ImageView控件的大小,但应将<ImageView>标签的android:scaleType属性值设为fitCenter。旋转图像可以用android.graphics.Matrix.setRotate方法来实现,通过该方法可以指定旋转的度数。

本节的例子提供了两个滚动条控件(SeekBar),第1个SeekBar控件(将在后面详细介绍)用于缩放图像(android:max属性值为240),第2个SeekBar控件用于旋转图像(android:max属性值为360,也就是说,通过该SeekBar控件可以使图像最多旋转360°)。

为了自适应屏幕的宽度(图像放大到与屏幕宽度相等时为止),在本例中没有直接将第1个SeekBar控件的android:max属性值设为240,而是使用如下代码来获得屏幕的宽度,并将屏幕宽度与图像的最小宽度(在本例中是minWidth变量,值为80)的差作为android:max属性的值。

DisplayMetrics dm = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(dm);

seekBar1.setMax(dm.widthPixels - minWidth);

在SeekBar类的onProgressChanged事件方法中需要控制图像的缩放和旋转,代码如下:

源代码文件:src/ch13/ResizeRoundImage/src/mobile/android/resize/round/image/Main.java

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)

{

  // 处理图像缩放

  if (seekBar.getId() == R.id.seekBar1)

  {

    int newWidth = progress + minWidth;       // 计算缩放后图像的新宽度

    int newHeight = (int) (newWidth * 3 / 4);     // 计算缩放后图像的新高度

    // 设置ImageView控件的尺寸

    imageView.setLayoutParams(new LinearLayout.LayoutParams(newWidth,newHeight));

    textView1.setText("图像宽度:" + newWidth + " 图像高度:" + newHeight);

  }

  // 处理图像旋转

  else if (seekBar.getId() == R.id.seekBar2)

  {

    // 装载dog.jpg文件,并返回该文件的Bitmap对象

    Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.dog)).

    getBitmap();

    // 设置图像的旋转角度

    matrix.setRotate(progress);

    // 旋转图像,并生成新的Bitmap对象

    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),

    matrix, true);

    // 重新在ImageView组件中显示旋转后的图像

    imageView.setImageBitmap(bitmap);

    textView2.setText(progress + "度");

  }

}

在编写上面代码时需要了解如下几点。

由于在本例中允许图像的最小宽度是80,因此,缩放后的新宽度应为seekBar1的当前进度与最小宽度(minWidth)之和。新高度可以根据新宽度计算出来。

由于图像的宽度和高度之比是4:3,因此,显示图像的ImageView控件的android:layout_width和android:layout_height属性的值的比例也应该是4:3,例如,在本例中这两个属性值分别是200dp和150dp,并且为了保证图像和ImageView控件的大小相同,android:scaleType属性值应设为fitCenter。

运行本例后,拖动第1个和第2个SeekBar控件对图像进行缩放和旋转,将显示如图13-21所示的效果。

 

▲图13-21 缩放和旋转图像