赞
踩
https://developer.android.google.cn/training/camerax/architecture
效果如图
(1)添加Google Maven 代码库
- buildscript {
-
- repositories {
- google()
- jcenter()
- }
- }
(2)添加java1.8
- android {
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- // For Kotlin projects
- kotlinOptions {
- jvmTarget = "1.8"
- }
- }
(3)添加camerax相关库,最新的版本号可以去maven 库官网查看
- //CameraX
- def camerax_version = "1.1.0-alpha06"
- // CameraX core library using camera2 implementation
- implementation "androidx.camera:camera-camera2:$camerax_version"
- // CameraX Lifecycle Library
- implementation "androidx.camera:camera-lifecycle:$camerax_version"
- // CameraX View class
- implementation "androidx.camera:camera-view:1.0.0-alpha24"
- <uses-feature android:name="android.hardware.camera.any" />
- <uses-permission android:name="android.permission.CAMERA" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
其中第一个是检查设备摄像头硬件的,.any代表前置或后置都可以
最后一个是录像要用,不录像的话可以不加,后面的动态权限也就不用询问了。
注:在询问权限回调中,如果用户选择
允许——grantResults == 0
始终允许——后续则不需要再询问权限了
禁止——grantResults == -1
禁止不再询问——其实你代码里还是询问了,只是他直接返回了grantResults == -1
- /**
- * 检查是否拥有权限
- */
- private void checkPermission(){
- if (Build.VERSION.SDK_INT >= 23) {//6.0以上才用动态权限
- boolean cameraPermission = hasPermission(Manifest.permission.CAMERA);
- boolean recordAudio = hasPermission(Manifest.permission.RECORD_AUDIO);
- if (cameraPermission && recordAudio) {
- startCamera();
- } else {
- requestPermissions(new String[]{Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO},CAMERA_PERMISSION_REQUEST_CODE);
- }
- }
- }
-
-
- /**
- *询问权限回调
- */
- @Override
- public void onRequestPermissionsResult(int requestCode,
- @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
- for (int i : grantResults) {
- if (i == -1) {
- //被禁止
- Toast.makeText(this,"获取相机或录像权限失败,请重新进入或手动设置权限!",Toast.LENGTH_SHORT).show();
- finish();
- }
- }
- startCamera();
- }
- }
- <androidx.camera.view.PreviewView
- android:id="@+id/act_cameraTest_pv_cameraPreview"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- /**
- * 开始预览
- */
- private void startCamera() {
- ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
- ProcessCameraProvider.getInstance(this);
- cameraProviderFuture.addListener(new Runnable() {
- @SuppressLint("RestrictedApi")
- @Override
- public void run() {
- try {
- //将相机的生命周期和activity的生命周期绑定,camerax 会自己释放
- ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
- Preview preview = new Preview.Builder().build();
- //创建图片的 capture
- mImageCapture = new ImageCapture.Builder()
- .setFlashMode(ImageCapture.FLASH_MODE_OFF)
- .build();
- //选择前置摄像头
- CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_FRONT).build();
- // Unbind use cases before rebinding
- cameraProvider.unbindAll();
-
- // Bind use cases to camera
- //参数中如果有mImageCapture才能拍照,否则会报下错
- //Not bound to a valid Camera [ImageCapture:androidx.camera.core.ImageCapture-bce6e930-b637-40ee-b9b9-
- mCamera = cameraProvider.bindToLifecycle(CameraTestActivity.this, cameraSelector, preview,mImageCapture);
- preview.setSurfaceProvider(pvCameraPreview.getSurfaceProvider());
- } catch (ExecutionException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }, ContextCompat.getMainExecutor(this));
- }
官方提供了两个拍照方法,下面是源码中的代码
第一个是获取相机预览图像的静态图片
第二个是直接保存到文件,形成一个.jpg照片的,比上面多一个参数
- /**
- * 获取静态图片
- */
- public void takeStaticPhoto(View view) {
- if (mImageCapture != null) {
- //开始拍照
- mImageCapture.takePicture(ContextCompat.getMainExecutor(this), new
- ImageCapture.OnImageCapturedCallback() {
- @Override
- public void onCaptureSuccess(ImageProxy image) {
- super.onCaptureSuccess(image);
- //ImageProxy 转 Bitmap
- mBitmap = BaseImageUtils.imageProxyToBitmap(image);
- imgShowStaticPhoto.setBackground(new
- BitmapDrawable(getApplicationContext().getResources(),mBitmap));
- //使用完image关闭
- image.close();
- }
-
- @Override
- public void onError(ImageCaptureException exception) {
- super.onError(exception);
- Log.d(TAG, "onError: ");
- }
- });
- }
- }
- /**
- * 拍照并存到存储空间
- * @param view
- */
- public void takeFilePhoto(View view) {
- if (mImageCapture != null) {
- File dir = new File(savePath);
- if (!dir.exists()) {
- dir.mkdirs();
- }
- //创建文件
- File file = new File(savePath,"CameraXPhoto.jpg");
- if (file.exists()) {
- file.delete();
- }
- //创建包文件的数据,比如创建文件
- ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
-
- //开始拍照
- mImageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
- @Override
- public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
- // Uri savedUri = outputFileResults.getSavedUri();
- Toast.makeText(CameraTestActivity.this, "照片保存成功:保存位置-我的手机/Android/data/com.test.cameraxdemo/files/photo ", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onError(@NonNull ImageCaptureException exception) {
- Toast.makeText(CameraTestActivity.this, "照片保存失败", Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
需要在上面的startCamera()方法中创建VideoCapture并绑定到Lifecycle中,与ImageCapture绑定的方式一样。即在cameraProvider.bindToLifecycle方法之前创建mVideoCapture,并在该方法中最后一个参数UserCase,加入mVideoCapture,如下所示:
- //创建图片的 capture
- mImageCapture = new ImageCapture.Builder()
- .setFlashMode(ImageCapture.FLASH_MODE_OFF)
- .build();
- mVideoCapture = new VideoCapture.Builder().build();
- //选择前置摄像头
- CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_FRONT).build();
- // Unbind use cases before rebinding
- cameraProvider.unbindAll();
-
- // Bind use cases to camera
- //参数中如果有mImageCapture才能拍照,否则会报下错
- //Not bound to a valid Camera [ImageCapture:androidx.camera.core.ImageCapture-bce6e930-b637-40ee-b9b9-
- mCamera = cameraProvider.bindToLifecycle(CameraTestActivity.this, cameraSelector, preview,mImageCapture,mVideoCapture);
用法跟拍照方法类似
- public void startVideo(View view) {
- if (TextUtils.equals("开始录制",btnRecord.getText().toString())) {
- btnRecord.setText("停止录制");
- startRecord();
-
- }else {
- btnRecord.setText("开始录制");
- stopRecord();
- }
- }
-
- @SuppressLint({"MissingPermission", "RestrictedApi"})
- private void startRecord() {
- if (mVideoCapture != null) {
- File dir = new File(savePath);
- if (!dir.exists()) {
- dir.mkdirs();
- }
- //创建文件
- File file = new File(savePath,"CameraXVideo.mp4");
- if (file.exists()) {
- file.delete();
- }
- VideoCapture.OutputFileOptions build = new VideoCapture.OutputFileOptions.Builder(file).build();
- mVideoCapture.startRecording(build, CameraXExecutors.mainThreadExecutor(), new VideoCapture.OnVideoSavedCallback() {
- @Override
- public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {
- Toast.makeText(CameraTestActivity.this, "视频保存成功:保存位置-我的手机/Android/data/com.test.cameraxdemo/files/camera ", Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
- Log.e(TAG, "onError: " + message);
- }
- });
- }
- }
调用mVideoCapture.stopRecording()方法,
注:因为VideoCapture绑定到了Lifecycle,所以如果你在调用前关闭了页面,它也会自动调用到该方法,并正常停止录像,生成mp4文件。
- @SuppressLint("RestrictedApi")
- private void stopRecord() {
- mVideoCapture.stopRecording();
- }
问题现象:
调用拍照方法报错
原因:
没有绑定ImageCapture
解决方法:
在startCamera()方法中有一段绑定到Lifecycle的代码
可以看到如果我把最后一个参数去掉代码不会报错也可以正常跑起来,因为这个方法源码是下图这样的,所以传参的时候传三个到多个都是可以的,如果报上面的错,应该是这里的ImageCapture参数没有传,加上之后可以解决这个问题。
问题现象:
连续调用两次mImageCapture.takePicture()后发现再调用就一直没有回应,如果你退出会进入onError回调,提示找不到camera。
这种情况下会发现控制台有两行提示语如下:
D/ImageCapture: Send image capture request [current, pending] = [0, 1]
W/ImageCapture: Too many acquire images. Close image to be able to process next.
- D/ImageCapture: Send image capture request [current, pending] = [0, 1]
- W/ImageCapture: Too many acquire images. Close image to be able to process next.
分析原因:
捕获的image太多了,需要关闭才能向下执行,那么去看下 ImageCapture 源码搜索下image.close(),发现一共有四处:两处是在catch的时候调用的,两处是在判断否的时候调用的,也就是说正常情况下拍照并成功返回之后并没有close,所以会造成这个问题。
解决办法:
在mImageCapture.takePicture()的成功回调函数中,等你使用完image之后手动把它关闭,这个问题就可以解决了。
- mImageCapture.takePicture(ContextCompat.getMainExecutor(this), new ImageCapture.OnImageCapturedCallback() {
- @Override
- public void onCaptureSuccess(ImageProxy image) {
- super.onCaptureSuccess(image);
- //ImageProxy 转 Bitmap
- Bitmap bitmap = imageProxyToBitmap(image);
- //使用完image关闭
- image.close();
- }
-
- @Override
- public void onError(ImageCaptureException exception) {
- super.onError(exception);
- Log.d(TAG, "onError: ");
- }
- });
需要源码的可以点个赞,点个关注,然后私信我。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。