本章将介绍有关Android上图像捕获和存储的基础知识。首先将探索Android所提供的内置功能,然后在后续章节中更多地介绍定制软件。我们将首先介绍如何利用内置的Camera(摄像头)应用程序,然后介绍如何利用MediaStore——内置的媒体和元数据存储机制 随着移动电话迅速成为移动计算机,它们在许多方面已经取代了各种各样的消费类电子产品。最早添加到移动电话上且和电话无关的硬件功能之一是摄像头。现在,似乎很难想象有人会购买一部不包含摄像头功能的移动电话。当然,基于Android的电话也不例外;从一开始,Android SDK就支持访问电话内置的硬件摄像头来捕获图像。 在Android上,完成许多事情的最便捷方式是通过使用意图(intent)来利用该设备上的某个现有软件。意图是Android的核心组件,在文档中将它解释为一个“将要执行的操作的描述”。在实践中,意图用于触发其他应用程序来完成某件事情,或者在单个应用程序的活动之间进行切换。 所有带有合适硬件(摄像头)的原版Android设备都会附带Camera应用程序。Camera应用程序包含一个意图过滤器(intent filter),它使得开发人员能够提供与Camera应用程序同等的图像捕获能力,而不必构建他们自己的定制捕获例程。 意图过滤器是程序员用于指定其应用程序能够提供某个特定功能的一种方法。在应用程序的AndroidManifest.xml 文件中指定一个意图过滤器,将会告诉Android,这个应用程序(尤其是包含意图过滤器的活动)将根据指令执行指定的任务。 Camera应用程序在其清单文件中指定了以下意图过滤器。这里显示的意图过滤器包含在“Camera”活动标记内。 <intent-filter> <action android:name="android.media.action.IMAGE_CAPTURE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> 为了通过一个意图利用Camera应用程序,我们所要做的仅仅是必须构造一个将由上述过滤器捕获的意图。 Intent i = new Intent("android.media.action.IMAGE_CAPTURE"); 在实践中,我们可能不希望直接使用动作字符串创建意图。在这种情况下,可以指定MediaStore类中的常量ACTION_IMAGE_CAPTURE。应该使用常量而非字符串本身的原因在于,如果该字符串发生了改变(当然常量也可能会不断地改变),那么使用常量将使得我们的调用比之前使用字符串更有利于未来的变化。 Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivity(i); 在一个基本的Android活动中使用这种意图,将导致默认的Camera应用程序以静止图片模式(still picture mode)启动,如图1-1所示。 图1-1 通过意图调用的内置Camera应用程序在模拟器中的运行结果 1.1从Camera应用程序返回数据 当然,在捕获一张图片时,如果Camera 应用程序没有将图片返回给调用活动,那么简单地使用内置的Camera应用程序捕获图像将不具有真正的作用。而为了使得它真正有用,可以将活动中的startActivity 方法替换为startActivityForResult 方法。使用该方法将允许我们访问从Camera应用程序中返回的数据,它恰好是用户以位图(Bitmap)形式捕获的图像。 以下是一个基本的示例。 package com.apress.proandroidmedia.ch1.cameraintent; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; public class CameraIntent extends Activity { final static int CAMERA_RESULT = 0; ImageView imv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent i = new Intent(android.provider .MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(i, CAMERA_RESULT); } protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (resultCode == RESULT_OK) { Get Bundle extras = intent.getExtras(); Bitmap bmp = (Bitmap) extras.get("data"); imv = (ImageView) findViewById(R.id.ReturnedImageView); imv.setImageBitmap(bmp); } } } 它需要在项目的layout/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="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/ReturnedImageView"android:layout_width= "wrap_content"android:layout_height="wrap_content"></ImageView> </LinearLayout> 为了完成上述示例,以下是AndroidManifest.xml 文件的内容。 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0"package="com.apress.proandroidmedia.ch1 .cameraintent"> <application android:icon="@drawable/icon" android:label= "@string/app_name"> <activity android:name=".CameraIntent" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> </manifest> 在此示例中,Camera应用程序在一个通过意图传递的附加值(extra)中返回图像,而该意图将在onActivityResult方法中传递给主调活动。附加值的名称为“data”,它包含一个Bitmap对象,需要从泛型对象将它强制转换过来。 //从意图中获取附加值 Bundle extras = intent.getExtras(); //从附加值中获取返回的图像 Bitmap bmp = (Bitmap) extras.get("data"); 在我们的布局XML (layout/main.xml)文件中,有一个ImageView对象。ImageView是泛型视图的扩展,其支持图像的显示。由于我们有一个带有指定ReturnedImageView编号(id)的ImageView对象,因此需要在活动中获得它的引用,并通过setImageBitmap方法将它的Bitmap对象设置为返回的图像。这将使得应用程序用户能够查看这幅捕获的图像。 为了获得ImageView对象的引用,使用在Activity类中指定的标准方法findViewById。该方法使得我们能够以编程方式引用在布局XML文件中指定的元素,我们正在通过将元素id传递给setContentView来使用该布局XML文件。上述示例在XML中以如下方式指定ImageView对象: <ImageView android:id="@+id/ReturnedImageView" android:layout_width= "wrap_content" android:layout_height="wrap_content"></ImageView> 为了引用ImageView并通知它显示来自Camera的Bitmap对象,使用以下代码。 imv = (ImageView) findViewById(R.id.ReturnedImageView); imv.setImageBitmap(bmp); 当运行这个示例时,您可能会注意到结果图像很小(在我的手机上,它的宽为121像素,高为162像素。其他设备会具有不同的默认大小)。这不是一个bug——相反,它是经过精心设计的。当通过一个意图触发时,Camera应用程序不会将全尺寸的图像返回给主调活动。通常,这样做需要大量的内存,而移动设备一般会在内存方面受限。相反,Camera应用程序将在返回的意图中返回一幅很小的缩略图,如图1-2所示。 图1-2 在ImageView 中显示的121×162像素的结果图像