Android多媒体开发 Pro Android Media 第二章 创建自定义相机应用 2

更改相机预览大小

Camera.Parameters中另一个特别有用设置是设置预览大小。正如使用其他设置,我们首先要查询的参数对象,取得其支持列表。取得预览尺寸列表之后,我们遍历它,以确保在设置之前,我们想要设置的大小是相机支持的。

在本示例中,我们不设定精确的预定尺寸,而选择一个设备支持,最接近且不大于预定值的尺寸。图 2-4 显示了此示例的输出。

...
public static final int LARGEST_WIDTH = 200;
public static final int LARGEST_HEIGHT= 200;
... 

如同所有的 Camera.Parameters,我们在SurfaceCreated中,打开相机和设置其预览显示Surface之后,获取和设置他们的值。 

public void surfaceCreated(SurfaceHolder holder) 
{
    camera = Camera.open();
    try {  
        camera.setPreviewDisplay(holder);
        Camera.Parameters parameters = camera.getParameters();

我们将用两个变量,记录最接近,但小于我们的预定大小的值。

        int bestWidth = 0;
        int bestHeight = 0; 

然后我们取得设备所支持的预览尺寸的列表。这将返回 Camera.Size 对象列表,供我们循环遍历。

        List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
        if (previewSizes.size() > 1)  
        {  
            Iterator<Camera.Size> cei = previewSizes.iterator();
            while (cei.hasNext())  
            { 
                Camera.Size aSize = cei.next(); 

如果列表的当前尺寸,大于我们保存的最佳尺寸,并且小于等于 LARGEST_WIDTH 和 LARGEST_HEIGHT,那么我们在 bestWidth 和 bestHeight 变量中保存当前高度和宽度,并继续检查。

                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;
               }
            }

我们遍历完相机支持的尺寸列表之后,我们肯定会得到某些值。如果我们的 bestHeight 和 bestWidth 变量等于 0,说明我们没能找到符合我们需要的尺寸,又或者Camera只支持一个固定的预览大小,这两种情况,我们不做处理。如果他们的值不为0,我们就以 bestWidth 和 bestHeight 为参数,调用 Camera.Parameters 的 setPreviewSize。

此外,我们还要告诉我们的相机预览 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();
    }
}

Android多媒体开发 Pro Android Media 第二章 创建自定义相机应用 2_第1张图片

图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) {  } 

OnPictureTaken 方法有两个参数。第一个是字节数组,为实际的JPEG图像数据,第二个是捕获图像的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();
}

在上述代码段,我们将新记录插入MediaStore,得到一个URI。我们随后可用该URI获取 OutputStream,用于 JPEG 数据写入。这会在 MediaStore 指定的位置创建一个文件,并将其链接到新记录。如果我们之后想要更新 MediaStore 记录中存储的元数据,我们可以用一个新的 ContentValues 对象来更新,如第 1 章中所述。

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);

最后,我们必须实际调用Camera.takePicture。为此,我们让预览屏幕变为可点击,在 onClick 方法中,我们进行拍摄。

我们让我们的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回调方法

除了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版本上的部分,在注释中做了声明。


这应该涵盖了建立一个自定义的基于camera的应用程序的基本知识。接下来,让我们看看如何扩展此应用程序,实现内置相机应用程序中不存在的功能。


你可能感兴趣的:(android,android,image,Camera,多媒体,摄像头)