更改相机预览大小

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


图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的应用程序的基本知识。接下来,让我们看看如何扩展此应用程序,实现内置相机应用程序中不存在的功能。


更多相关文章

  1. Android进阶——阿里Android开发手册学习笔记(一)
  2. AIDL --- Android中的远程接口[转]
  3. Android的icon设计指南
  4. Android菜鸟的成长笔记(4)——你真的理解了吗?
  5. Android研究之英特尔 Android* 开发人员指南上的对等应用详解
  6. android中基于网络和GPS的不同精度定位
  7. 一个现有Android工程作为组件加入到另一个Android工程最简便方法
  8. Android(安卓)仿淘宝选中商品不同尺寸的按钮组(一)
  9. Android深入学习之各种隐私权限判断和获取方法总结

随机推荐

  1. Android基本操作1(意图使用、界面转换、按
  2. 第十七章 Android 常见的Service
  3. 全家桶!阿里P8大佬熬夜15天,把所有Android
  4. Tensorflow在手机端的部署——官网Androi
  5. android SQLite存储简单范例+详细注释(增
  6. Android ImageView 图片等比缩放问题
  7. Google Docs 更新,提供 Honeycomb 平板更
  8. 《疯狂Android讲义》
  9. Android学习系列(39)--Android主题和样式
  10. Android UI Action Bar之ActionBarSherlo