在Android手机中,相机应该算是每部智能手机的标准配置了,google为了方便开发者开发,提供了一套供开发者使用的api,作为软件开发者基本上只要调用这些接口就可以进行Android相机方面的功能开发了(这里指的只是基本功能,比如预览、拍照等)!
在本篇博客中将想你展示如何使用google提供的接口(Camera1,后面还会说到,google在后期的版本中推出了Camera2接口,在下一篇博客中在介绍如何使用Camera2接口)开发自己的相机。在这里使用到的类不多,主要包含:Camera、SurfaceView、SurfaceHolder等!
google在相机预览、拍照包括人脸识别中都提供了一个接口,直接调用就可以实现相应的功能,是不是感觉很简单,下面我们就来实现一个Camera(具有预览拍照功能,拍照的图片储存在这里就不实现了)!
先看一下布局文件吧:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:background="@android:color/transparent"
android:id="@id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
FrameLayout>
<LinearLayout
android:layout_width="150dp"
android:layout_height="300dp"
android:layout_gravity="start|center_vertical"
android:background="@color/colorPrimary" />
android.support.v4.widget.DrawerLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@android:color/transparent">
<ImageView
android:onClick="onCapturePhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@android:color/transparent"
android:src="@drawable/photo_button" />
RelativeLayout>
RelativeLayout>
写的有点啰嗦,其实要做demo的话,只需要一个SurfaceView即可,其他的即可抛弃!!!
再来看一下java代码的实现:
package com.example.administrator.beercamera;
import android.app.Activity;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.util.Arrays;
public class Camera2Activity extends Activity {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private CameraManager mCameraManager;
private CameraDevice mCameraDeivce;
private CameraCaptureSession mCameraSession;
private CaptureRequest.Builder mRequestBuilder;
private ImageReader mImageReader;
private SavePhotoListener mSaveListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SurfaceViewCallBack());
mSaveListener = new SavePhotoListener();
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mCameraDeivce != null){
mCameraDeivce.close();
}
}
public void onCapturePhoto(View view) {
try {
final CaptureRequest.Builder mBuilder = mCameraDeivce.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
mBuilder.addTarget(mImageReader.getSurface());
mBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mBuilder.set(CaptureRequest.CONTROL_AWB_MODE,CaptureRequest.CONTROL_AWB_MODE_AUTO);
// mImageReader.setOnImageAvailableListener(mSaveListener,null);
mCameraSession.stopRepeating();
mCameraSession.capture((mBuilder.build()),new CameraResultCallBack(),null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private boolean CheckPermission(){
return true;
}
private class SurfaceViewCallBack implements SurfaceHolder.Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCameraManager.openCamera("0",new CameraCallBack(),null);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
private class CameraCallBack extends CameraDevice.StateCallback{
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.e("zyq","onOpened");
mCameraDeivce = camera;
try {
mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(),mSurfaceView.getHeight(), ImageFormat.JPEG,7);
mImageReader.setOnImageAvailableListener(mSaveListener,null);
mRequestBuilder = mCameraDeivce.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mRequestBuilder.addTarget(mSurfaceHolder.getSurface());
mCameraDeivce.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(),mImageReader.getSurface()),new CameraCaptureConfig(),null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
if(camera != null){
camera.close();
}
}
}
private class CameraCaptureConfig extends CameraCaptureSession.StateCallback{
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraSession = session;
mRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mRequestBuilder.set(CaptureRequest.CONTROL_AWB_MODE,CaptureRequest.CONTROL_AWB_MODE_AUTO);
try {
mCameraSession.setRepeatingRequest(mRequestBuilder.build(),null,null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}
private class CameraResultCallBack extends CameraCaptureSession.CaptureCallback{
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
super.onCaptureProgressed(session, request, partialResult);
Log.e("zyq","onCaptureProgressed");
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
mCameraSession = session;
Log.e("zyq","onCaptureCompleted");
try {
mCameraSession.setRepeatingRequest(mRequestBuilder.build(),null,null);
} catch (CameraAccessException e) {
Log.e("zyq","onCaptureCompleted e="+e.getMessage());
e.printStackTrace();
}
}
}
private class SavePhotoListener implements ImageReader.OnImageAvailableListener{
@Override
public void onImageAvailable(ImageReader reader) {
Log.e("zyq","save photo");
Image image = reader.acquireLatestImage();
if(image != null){
Log.e("zyq","iamge = "+image.getHeight()+" , "+image.getWidth());
}
}
}
}
具体预览的效果图就不给出了,给一下拍照时的log吧,log如下;
06-03 14:29:47.689 21208-21208/com.example.administrator.beercamera E/zyq: save photo
06-03 14:29:47.690 21208-21208/com.example.administrator.beercamera E/zyq: iamge = 1280 , 720
06-03 14:29:47.700 21208-21208/com.example.administrator.beercamera E/zyq: onCaptureCompleted
在ImageReader回调函数中,即使你不打算存储图片也需要调用获取Image的方法,不然的话,下一次点击拍照会出现不执行回调函数的情况,有兴趣的朋友可以自己试一下,使用Camera2刚开始可能会不太习惯,但是认真看一下之后,你会觉的Camera2的架构更容易理解。
下面大致说一下,具体的我也没有追过Camera2的架构代码:
使用Camera2的api,你会发现一切的操作基本上都是通过请求完成,而对请求结果的处理都是通过回调接口完成:
比如我们需要拍摄照片,那么就需要构建一个新的请求,在请求中指定请求的类型为抓取照片,请求放回数据的输出目标为ImageReader中的Surface,然后在请求中添加一系列camera的参数等,然后通过会话传递这个请求基本上就可以达到获取照片的目的了,详细的还是看一下上面的代码!!!
现在来看一下Camera1的API接口的使用:
package com.example.administrator.beercamera;
import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.io.IOException;
/**
* Created by Administrator on 2017/5/28 0028.
*/
@Deprecated
public class Camera1Activity extends Activity {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
private SavePhotoListener mSavePhotoListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new PreviewCallBack());
mSavePhotoListener = new SavePhotoListener();
}
public void onCapturePhoto(View view){
mCamera.takePicture(null,null,mSavePhotoListener);
}
private class PreviewCallBack implements SurfaceHolder.Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open(0);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mCamera != null){
mCamera.release();
}
}
private class SavePhotoListener implements Camera.PictureCallback{
@Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.i("zyq","data length = "+data.length);
mCamera.startPreview();
}
}
}
使用Camera1的时候,所有的操作都是一步接着一步的,首先打开相机,设置相关参数,开始预览、拍照等,这种思路很像我们平常操作手机中相机的步骤,感觉更符合我做事时思考的方式,但是Camera2肯定也有着它的优势,不然google没什么事干嘛推出Camera2呢!!
两者的区别主要是架构的不同,对于开发者来说,除了需要知道架构的不同外,我们还需要了解一下Camera2相关API的使用,不然还是不能很好的利用Camera2的接口进行相机编程的!!
好了关于Android相机这一块就说到这吧,其实要编写一个很好的Camera很是很难的,你需要考虑相机的各个方面的东西,兼容、设置、图片/视频的存储等,有条件的话可以看一下系统相机的源码,或者是MTK的相机,阅读源码还是很有收获的!!!有兴趣的朋友可以以关注我,遇到问题大家一起讨论一下!!
这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!