幸运的是,使用内置应用并不是访问摄像头的唯一途径。底层硬件的开放程度以及系统提供的访问方法,对我们和相机应用来说是一样的,我们可以在任意类型的应用中使用这些功能。
在这一章,我们将使用底层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" />
在开始使用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>
这是获取布局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.addCallback(this);
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();
try { camera.setPreviewDisplay(holder); } catch (IOException exception) { camera.release(); }
camera.startPreview(); }
public void surfaceDestroyed(SurfaceHolder holder) { camera.stopPreview(); camera.release(); }
图2-1. 摄像头预览,旋转了90度
发生旋转的原因是Camera假定方向是水平或横向的。纠正旋转最简单的方法是让我们的activity以横向模式出现。为此,我们在activity的onCreate方法中增加下面的代码。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
图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);
参数的设置位于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(); }
注意: 使用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);
图 2-3. 摄像头过度曝光预览图像
其他可能的效果也作为常量列在 Camera.Parameter 类中:
还存在类似常量, 分别用于antibanding、 闪光模式、 焦点模式、 场景模式和白平衡。