在上一章中,我们看了如何利用Android内置的相机应用为其他应用提供一个现成的照片拍摄组件。它提供标准界面给最终用户,对我们程序员而言又简单直接,不过它也因此缺乏灵活度。例如,如果我们想要我们的相机应用支持延时拍摄,使用内置应用就不好实现。

幸运的是,使用内置应用并不是访问摄像头的唯一途径。底层硬件的开放程度以及系统提供的访问方法,对我们和相机应用来说是一样的,我们可以在任意类型的应用中使用这些功能。

在这一章,我们将使用底层Camera类,创建一个拍照应用,以此来学习如何利用 Android 提供给我们的功能。为此我们将逐步创建一些不同的应用:

  • 简单的相机应用
  • 具有倒计时计时器的相机应用
  • 具备定时拍照功能的相机应用

Camera类的使用

我们使用Android的Camera类来访问设备上摄像头。我们使用它来采集图像,它的嵌套类Camera.Parameters来设置它的各种属性,如是否打开闪光灯,给白平衡设置一个合适的值
http://developer.android.com/reference/android/hardware/Camera.html

Camera权限

为了使用Camera类来拍摄照片,我们需要在AndroidManifest.xml文件中声明我们需要CAMERA权限。

<uses-permission android:name="android.permission.CAMERA" />

预览Surface

在开始使用Camera之前,我们还需创建某类Surface供Camera绘制取景器或者预览图像。Surface是一个抽象类,代表一个可以绘制图形或者图像的区域。取得一个可绘制的Surface的简单方法是使用SurfaceView。SurfaceView是一个具体类,它在标准的View中提供Surface。

要在布局中指定SurfaceView,我们只需在任何常规布局XML中使用<SurfaceView />即可。这里是一个基本的布局,在LinearLayout中指定了一个SurfaceView,供Camera做预览。

<?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"  >          <SurfaceView android:id="@+id/CameraView"         android:layout_width="fill_parent"                  android:layout_height="fill_parent">    </SurfaceView> </LinearLayout> 

在我们的代码中,为了将SurfaceView和Camera联系起来,我们需要把SurfaceHolder加进来。SurfaceHolder类扮演了Surface监视器的角色,通过回调接口,我们知道Surface什么时候创建,销毁,或者改变。SurfaceView类提供了函数getHolder,我们可以通过它方便地取得其Surface的SurfaceHolder。

这是获取布局XML中声明的SurfaceView,及其SurfaceHolder的代码片段。并且,它还将此Surface设置为Push型Surface,其意思是它的绘制缓冲区是由外部维护的。在这个例子中,缓冲区由 Camera 类管理的,Camera预览需要push型Surface。

SurfaceView cameraView = (CameraView) this.findViewById(R.id.CameraView);  SurfaceHolder surfaceHolder = cameraView.getHolder();  surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

另外,我们要在activity里实现SurfaceHolder.Callback接口。这样,我们的activity就能在Surface被创建,改变和被销毁时得到通知。为实现这个回调,我们加入下列方法。

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {}  public void surfaceCreated(SurfaceHolder holder) {}  public void surfaceDestroyed(SurfaceHolder holder) {} 

最后,我需要告知SurfaceHolder用此activity作为其Callback的实现类。

surfaceHolder.addCallback(this);

现在,我们的activity看起来应该像这样。

package com.apress.proandroidmedia.ch2.snapshot;  import android.app.Activity;  import android.os.Bundle;  import android.view.SurfaceHolder;  import android.view.SurfaceView;  public class SnapShot extends Activity implements SurfaceHolder.Callback {      SurfaceView cameraView;      SurfaceHolder surfaceHolder;      @Override  public void onCreate(Bundle savedInstanceState)  {          super.onCreate(savedInstanceState);        setContentView(R.layout.main);        cameraView = (SurfaceView) this.findViewById(R.id.CameraView);        surfaceHolder = cameraView.getHolder();        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);        surfaceHolder.addCallback(this);    }    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {    }    public void surfaceCreated(SurfaceHolder holder) {    }    public void surfaceDestroyed(SurfaceHolder holder) {    }} 

相机实现

现在,activity和预览Surface都已经就绪,我们可以开始使用Camera对象了。当Surface被创建时,因SorfaceHolder.Callback的缘故,触发surfaceCreated方法的调用。在该函数中,我们通过调用Camera的静态函数open,取得一个Camera对象。

Camera camera;public void surfaceCreated(SurfaceHolder holder) {      camera = Camera.open();

接着,我们将Camera的预览设置到由回调函数传入的SurfaceHolder中显示。调用设置函数需要包含在try catch块中,因为它可能会抛出一个IOException。如果真有异常抛出,我们要释放Camera,否则我们占用摄像头硬件资源,会影响其他应用使用。

    try     {        camera.setPreviewDisplay(holder);      }      catch (IOException exception)      {          camera.release();    }

最后,我们启动Camera预览。

    camera.startPreview();}

同样,在surfaceDestoryed中,我们也要释放Camera。我们首先调用stopPreview,确保完成所有清理工作。

public void surfaceDestroyed(SurfaceHolder holder) {   camera.stopPreview();   camera.release();}

运行这段代码,你可能会发现预览画面有些奇怪。预览图像逆时针旋转了90度,如图2-1所示。

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

图2-1. 摄像头预览,旋转了90度

发生旋转的原因是Camera假定方向是水平或横向的。纠正旋转最简单的方法是让我们的activity以横向模式出现。为此,我们在activity的onCreate方法中增加下面的代码。

    @Override      public void onCreate(Bundle savedInstanceState)   {       super.onCreate(savedInstanceState);       setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

现在,我们的相机预览正常显示了,如图2-2. 不幸的是,我们的应用停留在了横向模式

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

图2-2. 摄像头横向预览

设置摄像头参数

前面提到过,Camera类有一个嵌套类Camera.Parameters。这个类有一系列重要的属性或者设置可以用来改变Camera的运行。其中一个能马上帮助我们解决预览的旋转、横向问题。

相机使用的参数可以像下面这样修改:

Camera.Parameters parameters = camera.getParameters();parameters.set("some parameter", "some value");// 或者parameters.set("some parameter", some_int);camera.setParameters(parameters);

有两种不同的通用的 Parameters.set 方法。第一个采用采用字符串作为参数名称和值,第二个采用字符串作名称但值是一个整数。

参数的设置位于surfaceCreated方法中,放在Camera创建并指定其预览Surface之后。这里演示了我们如何使用参数来请求相机纵向而非横向显示。

public void surfaceCreated(SurfaceHolder holder){    camera = Camera.open();    try {        Camera.Parameters parameters = camera.getParameters();        if (this.getResources().getConfiguration().orientation             !=  Configuration.ORIENTATION_LANDSCAPE)       {            // 这是一个没有文档说明,但是广为人知的功能            parameters.set("orientation", "portrait");                      // Android 2.2 及以上版本            //camera.setDisplayOrientation(90);            // Android 2.0及以上,可取消注释            //parameters.setRotation(90);        }        else         {             // 这是一个没有文档说明,但是广为人知的功能             parameters.set("orientation", "landscape");              // Android 2.2 及以上版本             //camera.setDisplayOrientation(0);              // Android 2.0及以上,可取消注释             //parameters.setRotation(0);        }            camera.setParameters(parameters);      camera.setPreviewDisplay(holder);      }    catch (IOException exception)    {        camera.release();        Log.v(LOGTAG,exception.getMessage());    }    camera.startPreview();}

上述代码首先检查该设备的配置 (通过调用 Context.getResources().getConfiguration()) 来看看当前的方向是什么。如果不是横向,它将设置 Camera.Parameters 的"orientation" 为 "portrait"。此外,调用 Camera.Parameters 的 setRotation 方法,传入参数90度。这个方法,包含在 API 级别 5 (版本 2.0)及以上版本,它实际没有做任何旋转 ;而是告诉 Camera,在 EXIF 数据中指定图像在显示时,旋转 90 度。如果不写这一行,在其他应用程序中查看此图像时,它可能会横向显示。

注意: 使用Camera.Parameter来修改Camera旋转属性的方法,针对的是Android 2.1 及以下版本。在 Android 2.2 版本,Camera类引入了一个新方法:setDisplayOrientation(int degrees)。这种方法接受一个整数做参数,表示图像应旋转的度数。有效的度数只有0、 90、 180、 270。

大多数可以或者应当修改的参数,都有特定的方法与之关联。我们可以从 setRotation 方法看到,它们遵循的 Java getter 和 setter 设计模式。例如,设置相机的闪光模式可以用getFlashMode (Camera.Parameters.FLASH_MODE_AUTO),获取当前值可以用 getFlashMode(),而不必通过通用Parameters.set 方法来完成。

从 Android 2.0开始,一个有趣的属性允许我们更换颜色效果。现在我们用它来做演示。它的Getter 和 setter 是 getColorEffect 和 setColorEffect。还有一个getSupportedColorEffects 方法,返回一个字符串对象列表,表示指定设备支持的各种颜色效果。事实上,所有具有 getter 和 setter方法的属性都有跟这个类似的方法,用于确保在使用该功能之前,该功能可用。

Camera.Parameters parameters = camera.getParameters();List<String> colorEffects = parameters.getSupportedColorEffects();Iterator<String> cei = colorEffects.iterator();while (cei.hasNext()){    String currentEffect = cei.next();    Log.v("SNAPSHOT","Checking " + currentEffect);    if (currentEffect.equals(Camera.Parameters.EFFECT_SOLARIZE))    {        Log.v("SNAPSHOT","Using SOLARIZE");        parameters.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);        break;    }}Log.v("SNAPSHOT","Using Effect: " + parameters.getColorEffect());camera.setParameters(parameters);

在上述代码中,我们首先通过 getSupportedColorEffect 方法查询 Camera.Parameters 对象支持哪些效果。然后我们使用一个迭代器来遍历效果列表,检查其中是否有我们想要的效果,这个例子中我们要的是 Camera.Parameters.EFFECT_SOLARIZE。如果它出现在列表中,那么表示 Camera 是支持它的。我们可以继续向前,调用 Camera.Parameters 对象的setColorEffect方法,传入曝光过度参数。图 2-3 显示 Camera.Parameters.EFFECT_SOLARIZE 生效。

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

图 2-3. 摄像头过度曝光预览图像

其他可能的效果也作为常量列在 Camera.Parameter 类中:

  • EFFECT_NONE
  • EFFECT_MONO
  • EFFECT_NEGATIVE
  • EFFECT_SOLARIZE
  • EFFECT_SEPIA
  • EFFECT_POSTERIZE
  • EFFECT_WHITEBOARD
  • EFFECT_BLACKBOARD
  • EFFECT_AQUA

还存在类似常量, 分别用于antibanding、 闪光模式、 焦点模式、 场景模式和白平衡。


更多相关文章

  1. 详解Android中IntentService的使用方法
  2. ReactNative调用Android原生方法
  3. Cocos项目避免不断复制粘贴android lib库的方法
  4. Android去除系统自带动画的两种方法
  5. android studio中建立assets和jnilibs的方法
  6. Android unspecified' depends on one or more Android Librarie
  7. android控件-ImageView使用方法整理
  8. android的listview 嵌套在 ViewPage 多次调用getview原因和解决
  9. Android提交数据到服务器的两种方式四种方法

随机推荐

  1. Android(安卓)Factory Reset Without Los
  2. Android(安卓)跳转界面 自动弹出输入框
  3. Android(安卓)Java COOKIES
  4. Android(安卓)读取SIM卡参数
  5. Android(安卓)Activities and Tasks seri
  6. android在处理一写图片资源
  7. Android(安卓)上传文件[转]
  8. android图片拖动
  9. android获取3G或wifi流量信息
  10. 2013.12.03(6) ——— android ActionbarSh