Camera 官方文档翻译
Manifest 声明
在使用 Camera API 开发应用前,你需要确认 Manifest 已经包含允许使用相机硬件和其他相关特性的声明。
- 相机权限 -你的应用必须申请使用相机的权限
<uses-permission android:name="android.permission.CAMERA" />
- 相机特性 -你的应用也必须声明相机特性的使用,例如:
<uses-feature android:name="android.hardware.camera" />
(译者注: <uses-feature> 的具体使用方法不是本文关心的重点,需要读者自己查阅)
- 存储权限 -如果你的应用需要在设备的 externel storage 中保存图片或者视频,你也需要声明以下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 音频采集权限 -录制视频时需要采集音频时,需要声明音频采集权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
···
- 位置权限 -如果你需要用GPS位置信息标志图片,你需要获取 ACCESS_FINE_LOCATION 权限。如果你的应用运行在 Android 5.0 以上,你还需要声明应用使用了设备 GPS:
```java
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
构建一个相机应用
以下引导使用 Camera API.
创建一个自定义的相机接口主要分为几步:
检测并获取相机 -检测相机是否存在并获取
创建一个预览类 -创建一个继承自 SurfaceView 的类并实现 SurfaceHolder 接口。这个类展示相机的实时图像。
构建一个预览界面 -创建完相机预览累以后,创建一个布局以展示预览图和需要的 UI 界面。
创建采集的监听 -连接控制开始采集图片和视频的接口以响应用户的操作,比如按下按钮。
采集并保存文件 -采集图片或视频并保存输出。
释放相机 -使用完相机以后,你的应用必须正确的释放相机以供其他应用使用。
检测相机硬件
如果你的应用没有使用 manifest 的 Camera 的 <uses-feature> 声明,你应该检查运行时相机是否可用。使用 PackageManager.hasSystemFeature() 方法检查,示例代码如下:
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
Android 设备可以有多个摄像头,比如一个用于拍照的后置摄像头和视频电话的前置摄像头。 Android 2.3 及以上,你可以使用 Camera.getNumberOfCameras() 方法检查设备中可用相机数。
获取相机
如果你确定设备有相机硬件,你必须通过得到相机的实例来获取(除非你使用 Intent 获取相机)。
使用 Camera.open() 方法获取基础的相机,示例代码如下:
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
在 Android 2.3 以上,你可以使用 Camera。open(int) 获取特定的相机。以上的示例代码将在有多个相机的设备上获取第一个、后置摄像头。
检查相机特性
得到相机实例以后,你可以使用 Camera.getParameters() 方法获取关于相机的更多信息并检查返回而来的 Camera。Parameters 对象。在 API 9 及以上,使用 Camera.getCameraInfo() 检查相机在设备的前/后,及图像的方向。
创建一个预览类
以下的实例代码展示了如何创建一个基本的相机预览类。
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
如果想要设置相机预览的大小,在 surfaceChanged() 方法中设置。设置预览大小时,你必须使用 getSupportedPreviewSizes() 中的值。不能随便设置其他值。
在布局中写入预览界面
以下布局代码提供了一个用来展示相机预览界面的视图。在示例中, FrameLayout 元素用来作为相机预览类的容器。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<Button
android:id="@+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>
在大多数设备上,相机的默认方向为横屏。这个示例中制定了一个水平布局切一下的代码制定了应用的方向为水平。
<activity android:name=".CameraActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<!-- configure this activity to use landscape orientation -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
注意:相机预览不一定必须水平布局。从 API 8 开始,你可以使用 setDisplayOrientation() 方法设置预览图片的旋转角度。当用于改变手机方向时,你可以在预览类的 surfaceChanged() 方法中改变预览方向,首先用 Camera.stopPreview() 方法停止预览并改变方向,然后使用 Camera.startPreview() 方法开始预览。
public class CameraActivity extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
拍照
使用 Camera.takePicture() 方法可以拍摄照片。这个方法传入三个从相机接收的三个参数。必须实现 Camera。PictureCallback 接口接收 JPEG 格式的数据并写入文件。
private PictureCallback mPicture = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " +
e.getMessage());
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
···
```java
// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
);