Camera.Parameters中另一个特别有用设置是设置预览大小。正如使用其他设置,我们首先要查询的参数对象,取得其支持列表。取得预览尺寸列表之后,我们遍历它,以确保在设置之前,我们想要设置的大小是相机支持的。
在本示例中,我们不设定精确的预定尺寸,而选择一个设备支持,最接近且不大于预定值的尺寸。图 2-4 显示了此示例的输出。
... public static final int LARGEST_WIDTH = 200; public static final int LARGEST_HEIGHT= 200; ...
public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); try { camera.setPreviewDisplay(holder); Camera.Parameters parameters = camera.getParameters();
int bestWidth = 0; int bestHeight = 0;
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes(); if (previewSizes.size() > 1) { Iterator<Camera.Size> cei = previewSizes.iterator(); while (cei.hasNext()) { Camera.Size aSize = cei.next();
Log.v("SNAPSHOT","Checking " + aSize.width + " x " + aSize.height); if (aSize.width > bestWidth && aSize.width <= LARGEST_WIDTH && aSize.height > bestHeight && aSize.height <= LARGEST_HEIGHT) { // 到目前为止它是最大的而且没有超过屏幕尺寸 bestWidth = aSize.width; bestHeight = aSize.height; } }
此外,我们还要告诉我们的相机预览 SurfaceView 对象 cameraView,以此大小显示预览。如果SurfaceView 不改变大小,相机的预览图像将被扭曲或以极低的质量显示。
if (bestHeight != 0 && bestWidth != 0) { Log.v("SNAPSHOT", "Using " + bestWidth + " x " + bestHeight); parameters.setPreviewSize(bestWidth, bestHeight); cameraView.setLayoutParams(new LinearLayout.LayoutParams( bestWidth,bestHeight)); } } camera.setParameters(parameters);
} catch (IOException exception) { camera.release(); } }
图2-4. 相机以小尺寸预览
使用Camera类捕获图像,我们得调用takePicture方法。此方法接收三个或四个参数,都为回调函数。使用takePicture方法的最简单形式是所有参数都设为 null。但是在拍摄了图像之后,没法得到图像的引用。至少要实现其中的一个回调函数。最安全的是 Camera.PictureCallback.onPictureTaken。当图像采集压缩就绪后,它一定会被调用。为此,我们让我们的activity实现Camera.PictureCallback接口,添加onPictureTaken方法。
public class SnapShot extends Activity implements SurfaceHolder.Callback, Camera.PictureCallback { public void onPictureTaken(byte[] data, Camera camera) { }
因为给我们的是实际的 JPEG 数据,我们要保存它,只需要将它写到磁盘的某个地方就行了。我们已经知道,利用 MediaStore 指定它的位置和元数据是个好主意。
当onPictureTaken方法被调用时,我们需要调用Camera对象的startPreview。因为当takePicture方法被调用时,预览就自动暂停了。回调函数告诉我们,现在可以重新启动预览了。
public void onPictureTaken(byte[] data, Camera camera) { Uri imageFileUri = getContentResolver(). insert(Media.EXTERNAL_CONTENT_URI, new ContentValues()); try { OutputStream imageFileOS = getContentResolver().openOutputStream(imageFileUri); imageFileOS.write(data); imageFileOS.flush(); imageFileOS.close(); } catch (FileNotFoundException e) { } catch (IOException e) { } camera.startPreview(); }
ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, "This is a test title"); contentValues.put(Media.DESCRIPTION, "This is a test description"); getContentResolver().update(imageFileUri,contentValues,null,null);
我们让我们的activity实现 OnClickListener 并设置surfaceView 的 onClickListener 为activity。然后,通过setClickable(true),我们让SurfaceView成为可点击的。此外,我们需要使 SurfaceView 可获得焦点。SurfaceView 在默认情况下是不能获得焦点的,所以我们必须通过 setFocusable(true) 显式设置。此外,当我们在"触摸模式"时,焦点一般是禁用的,因此我们必须通过显性调用 setFocusInTouchMode(true) 使能焦点。
public class SnapShot extends Activity implements OnClickListener, SurfaceHolder.Callback, Camera.PictureCallback { ... public void onCreate(Bundle savedInstanceState) { ... cameraView.setFocusable(true); cameraView.setFocusableInTouchMode(true); cameraView.setClickable(true); cameraView.setOnClickListener(this); } public void onClick(View v) { camera.takePicture(null, null, null, this); }
除了Camera.PictureCallback,还有其他一些回调方法也值得提出来。
Camera.PreviewCallback: 定义了方法onPreviewFrame(byte[] data, Camera camera),当预览帧就绪时被调用。函数传入一字节数组,包含图像当前的像素值。Camera有三种使用此回调的方法。
setPreviewCallback(Camera.PreviewCallback): 使用此方法注册Camera.PreviewCallback,可以确保每当一个新的预览帧就绪调用onPreviewFrame,并显示在屏幕上 。传递给 onPreviewFrame 的数据字节数组,最可能是YUV格式。不幸的是,直到Android 2.2,才配备了 YUV 格式解码器 (YuvImage) ;在以前版本中,解码必须手工完成。
setOneShotPreviewCallback(Camera.PreviewCallback): 使用此方法注册 Camera.PreviewCallback,当下一个预览图像就绪时,会调用onPreviewFrame一次。同样, 传递给onPreviewFrame的预览图像数据最可能是YUV格式。可以通过定义在 ImageFormat 中的常量与 Camera.getParameters().getPreviewFormat() 返回值进行比较得出。
setPreviewCallbackWithBuffer(Camera.PreviewCallback): 在 Android 2.2 中引入,这种方法跟正常的setPreviewCallback 工作方式相同,但需要我们指定一个字节数组,作为预览图像数据缓冲区。这样做是为了在处理预览图像时,我们能更好的管理内存。
Camera.AutoFocusCallback: 定义了方法onAutoFocus,自动对焦行为完成时被调用。调用Camera对象的autoFocus方法,以这个回调接口的实例作为参数,可能会触发自动对焦。
Camera.ErrorCallback: 定义了方法onError,Camera发生错误时被调用。有两个常量可以与传入的错误代码进行比较CAMERA_ERROR_UNKNOWN和MERA_ERROR_SERVER_DIED.
Camera.OnZoomChangeListener: 定义了方法onZoomChange方法,当正在进行或完成"平滑缩放" (缓慢放大或缩小)时被调用。在 Android 2.2(API 级别 8)中介绍了这个类及其方法。
Camera.ShutterCallback: 定义 onShutter 方法,在拍摄图像时被调用。
让我们完成整个示例。下面的代码要运行在Android 2.2 及以上版本,但做细小修改,它可以在 1.6 及更高版本上运行。需要运行在1.6版本上的部分,在注释中做了声明。