Android开发中关于Camera2 API的具体使用方法
Camera2 API的使用方法
一、开发环境
- Windows10
- Android Studio 4.0.1
- JDK 1.8
- 谷歌官方API手册 https://developer.android.com/reference/android/hardware/camera2/package-summary
二、开发过程
新建测试工程 在布局文件中添加TextureView用于显示Camera预览画面 清单文件中添加必要权限
<TextureView
android:id="@+id/texture_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextureView>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
完整布局代码 添加三个按钮
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/texture_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextureView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<Button
android:id="@+id/switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="切换相机">
</Button>
<Button
android:id="@+id/capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="拍照">
</Button>
<Button
android:id="@+id/ratio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="比例">
</Button>
</LinearLayout>
</RelativeLayout>
在Activity中实现接口TextureView.SurfaceTextureListener 复写四个方法
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
初始化相机
用到 CameraManager CameraDevice CameraCaptureSession CaptureRequest.Builder CameraCharacteristics这几个类 详解在https://www.cnblogs.com/fuyaozhishang/p/9752581.html 架构图也很清晰
首先获取相机服务
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
获取相机属性 currentCamera为String类型 自行定义 默认后置摄像头为0,前置为1
CameraCharacteristics characteristics = manager.getCameraCharacteristics(currentCamera);
获取相机的预览尺寸和拍照尺寸 我设定的宽高比例为4:3 也可设置为16:9
StreamConfigurationMap scmap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
previewSizes = scmap.getOutputSizes(ImageReader.class);
for (int i = 0; i < previewSizes.length; i++) {
if (previewSizes[i].getHeight() == ScreenWidth) {
if ((previewSizes[i].getWidth() * 1.0 / previewSizes[i].getHeight() * 1.0) == 4.0 / 3.0) {
WIDTH = previewSizes[i].getHeight();
HEIGHT = previewSizes[i].getWidth();
}
}
}
ImageReader类用来绑定预览数据 设置照片分辨率 以及设置预览数据回调 以及最后的保存照片
ImageReader mImageReader = ImageReader.newInstance(previewSizes[0].getWidth(), previewSizes[0].getHeight(), ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
showImage(reader);
}
};
private void showImage(ImageReader reader) {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
matrix.postRotate(ORIENTATION.get(rotation));//前后摄像头方向处理
if (currentCamera.equals("1")) {//处理前置摄像头镜像问题
matrix.postScale(-1, 1);
}
// 拿到图片数据
Image image = reader.acquireLatestImage();
// 获取字节缓冲
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
// 创建与缓冲区相同的字节数组
byte[] bytes = new byte[buffer.remaining()];
// 将数据读取字节数组
buffer.get(bytes);
// 创建图片
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Bitmap newBM = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
saveBitmap(newBM);
image.close();
}
设置TextureView的宽高
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(WIDTH, HEIGHT);
mTextureView.setLayoutParams(layoutParams);
mTextureView.setSurfaceTextureListener(this);
回调SurfaceTextureListener接口,在onSurfaceTextureAvailable方法中申请权限,如果申请权限的时机错误,则会出现第一次进入app后授权成功,但是相机预览画面依然空白的情况。这里使用PermissionX申请权限框架,感谢郭霖大神。
PermissionX.init(Camera2Activity.this)
.permissions(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE)
.explainReasonBeforeRequest()
.onExplainRequestReason(new ExplainReasonCallback() {
@Override
public void onExplainReason(ExplainScope scope, List<String> deniedList) {
scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "我已明白", "取消");
}
})
.onForwardToSettings(new ForwardToSettingsCallback() {
@Override
public void onForwardToSettings(ForwardScope scope, List<String> deniedList) {
scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "我已明白", "取消");
}
})
.request(new RequestCallback() {
@SuppressLint("MissingPermission")
@Override
public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
if (allGranted) {
try {
manager.openCamera(currentCamera, mCameraStateCallBack, null);//开启相机,回调开启状态
} catch (CameraAccessException e) {
e.printStackTrace();
}
} else {
Toast.makeText(Camera2Activity.this, "拒绝授权", Toast.LENGTH_SHORT).show();
}
}
});
private CameraDevice.StateCallback mCameraStateCallBack = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
openPreview(HEIGHT, WIDTH);//开启相机预览,传入宽高
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
}
};
public void openPreview(int height, int width) {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(height, width);
Surface surface = new Surface(texture);//创建预览视图
try {
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//相机设置为预览模式
mPreviewRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mCameraCaptureCallBack, null);//将预览数据绑定到TextureView
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
开始预览,回调方法
private CameraCaptureSession.StateCallback mCameraCaptureCallBack = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
mCameraCaptureSession = session;
mPreviewRequestBuilder= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(mPreviewSurface);
mCameraCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
};
拍照
try {
//首先我们创建请求拍照的CaptureRequest
final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
//获取屏幕方向
int rotation = getWindowManager().getDefaultDisplay().getRotation();
mCaptureBuilder.addTarget(mImageReader.getSurface());
//设置拍照方向
mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));
//停止预览
mCameraCaptureSession.stopRepeating();
//开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片
CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
Toast.makeText(Camera2Activity.this, "拍照成功!", Toast.LENGTH_SHORT).show();
try {
mCameraCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
};
mCameraCaptureSession.capture(mCaptureBuilder.build(), captureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
切换摄像头
switch (currentCamera) {
case "0":
try {
mCameraCaptureSession.close();
mCameraDevice.close();
currentCamera = "1";
initCamera();
manager.openCamera(currentCamera, mCameraStateCallBack, null);
front();//矫正传感器方向
} catch (CameraAccessException e) {
e.printStackTrace();
}
break;
case "1":
try {
mCameraCaptureSession.close();
mCameraDevice.close();
currentCamera = "0";
initCamera();
manager.openCamera(currentCamera, mCameraStateCallBack, null);
rear();//矫正传感器方向
} catch (CameraAccessException e) {
e.printStackTrace();
}
break;
}
还有一些小问题比如按home键返回桌面再次进入程序的时候相机卡住,别的程序占用相机导致相机卡住等等问题都已经解决,源代码我会开源到github参考。地址 https://github.com/sata21-lab/Camera2
本文地址:https://blog.csdn.net/sata21_21/article/details/107782365
推荐阅读
-
Android开发之OkHttpUtils的具体使用方法
-
Android开发中FileProvider的使用方法
-
Android 开发中关于摄像头方向的理解
-
关于基于Linphone的视频通话Android端开发过程中遇到的问题
-
Android开发中关于Camera2 API的具体使用方法
-
Android开发中简单的launcher概念及使用方法介绍
-
关于Android官方开发教程中EditText editText = (EditText) findViewById(R.id.editText);报错的解决办法
-
Android开发中关于属性系统的简单介绍
-
Android开发中关于组件导出的风险及防范
-
php开发过程中关于继承的使用方法分享_php技巧