如何调用设备摄像头进行拍照、预览并将拍摄结果保存在媒体库中(Camera)("手机拍照全攻略:调用摄像头拍照、实时预览及保存照片到媒体库")
原创
一、引言
在移动应用开发中,调用设备摄像头进行拍照和预览是一个常见的需求。本文将详细介绍怎样在Android和iOS平台上使用原生API调用摄像头进行拍照、实时预览以及将拍摄最终保存在媒体库中。我们将涵盖从摄像头权限申请、摄像头配置、拍照、预览到保存照片的完整流程。
二、摄像头权限申请
在调用摄像头之前,首先需要申请相应的权限。以下是Android和iOS平台上申请摄像头权限的代码示例。
Android平台权限申请
// 在AndroidManifest.xml中添加以下权限
// 在代码中动态申请权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_CAMERA_REQUEST_CODE);
}
iOS平台权限申请
// 在Info.plist中添加以下权限
NSCameraUsageDescription
NSPhotoLibraryUsageDescription
// 在代码中动态申请权限
AVAuthorizationStatus cameraAuthorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (cameraAuthorizationStatus == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
// 权限被授予
} else {
// 权限被拒绝
}
}];
}
三、摄像头配置与预览
在申请到权限后,接下来需要配置摄像头并进行预览。以下是Android和iOS平台上摄像头配置和预览的代码示例。
Android平台摄像头配置与预览
// 创建Camera对象
Camera camera = Camera.open();
// 配置摄像头参数
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(640, 480);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
camera.setParameters(parameters);
// 设置摄像头预览的SurfaceView
SurfaceView surfaceView = findViewById(R.id.camera_preview);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 如果预览可以更改或旋转,请在此处处理这些事件。确保重新开端预览。
if (surfaceHolder.getSurface() == null) {
return;
}
try {
camera.stopPreview();
} catch (Exception e) {
// 忽略:尝试停止不存在的预览
}
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 清理和释放资源
camera.stopPreview();
camera.release();
camera = null;
}
});
iOS平台摄像头配置与预览
// 创建AVCaptureSession对象
AVCaptureSession *session = [[AVCaptureSession alloc] init];
// 选择摄像头
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 创建 AVCaptureDeviceInput 对象并添加到会话中
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if ([session canAddInput:input]) {
[session addInput:input];
} else {
// 处理差错
}
// 创建 AVCaptureVideoPreviewLayer 对象并添加到视图
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
previewLayer.frame = self.view.bounds;
[self.view.layer addSublayer:previewLayer];
// 开端预览
[session startRunning];
四、拍照
在摄像头预览正常后,接下来我们将实现拍照功能。以下是Android和iOS平台上拍照的代码示例。
Android平台拍照
// 拍照的回调方法
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// 保存照片到媒体库
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 通知媒体库更新
MediaScannerConnection.scanFile(context, new String[]{pictureFile.toString()}, null, null);
}
};
// 拍照
camera.takePicture(null, null, pictureCallback);
iOS平台拍照
// 创建 AVCapturePhotoOutput 对象并添加到会话中
AVCapturePhotoOutput *photoOutput = [[AVCapturePhotoOutput alloc] init];
if ([session canAddOutput:photoOutput]) {
[session addOutput:photoOutput];
} else {
// 处理差错
}
// 拍照
[photoOutput capturePhotoWithSettings:[AVCapturePhotoSettings new]
delegate:self];
五、保存照片到媒体库
在拍照完成后,我们需要将照片保存到媒体库中,以便用户可以在相册中查看。以下是Android和iOS平台上保存照片到媒体库的代码示例。
Android平台保存照片到媒体库
private static File getOutputMediaFile(int type) {
// 确保外部存储可用
if (!isExternalStorageAvailable()) {
return null;
}
// 创建媒体存储目录
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyApp");
// 创建一个以当前时间戳命名的媒体文件
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_" + timeStamp + ".jpg");
} else {
return null;
}
return mediaFile;
}
private static boolean isExternalStorageAvailable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
iOS平台保存照片到媒体库
// 拍照完成的回调方法
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)sampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef *)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error {
// 从sampleBuffer中获取图片数据
NSData *imageData = [AVCapturePhotoOutput AVCapturePhotoOutputCopyImageData sampleBuffer:sampleBuffer];
// 将图片数据变成UIImage
UIImage *image = [[UIImage alloc] initWithData:imageData scale:1.0 orientation:UIImageOrientationUp];
// 保存图片到媒体库
if ([UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:], NULL)]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"保存胜利" message:@"照片已保存到相册" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"保存落败" message:@"照片未能保存到相册" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
}
}
// 图片保存完成的回调方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
if (error == nil) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"保存胜利" message:@"照片已保存到相册" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"保存落败" message:@"照片未能保存到相册" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
}
}
六、总结
本文详细介绍了在Android和iOS平台上调用摄像头进行拍照、实时预览以及将拍摄最终保存在媒体库中的完整流程。通过掌握这些技术,开发者可以轻松实现移动应用中的拍照功能,并提供更好的用户体验。需要注意的是,摄像头API大概会随着操作系统版本的更新而出现变化,由此开发者需要关注官方文档,确保代码的兼容性。