Android CameraX使用,预览拍照获取静态图像

Posted break妖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android CameraX使用,预览拍照获取静态图像相关的知识,希望对你有一定的参考价值。

1.想要了解介绍的参考官网:

https://developer.android.google.cn/training/camerax/architecture

2.实现预览

效果如图

2.1第一步:引入依赖

(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"

2.2第二步:添加权限

<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" />

其中第一个是检查设备摄像头硬件的,.any代表前置或后置都可以

2.3第三步:6.0后动态权限

注:在询问权限回调中,如果用户选择

允许——grantResults == 0

始终允许——后续则不需要再询问权限了

禁止——grantResults == -1

禁止不再询问——其实你代码里还是询问了,只是他直接返回了grantResults == -1

/**
 * 检查是否拥有权限
 */
private void checkPermission(){
    if (Build.VERSION.SDK_INT >= 23) {//6.0以上才用动态权限
        boolean cameraPermission = hasPermission(Manifest.permission.CAMERA);
        if (cameraPermission) {
            startCamera();
        } else {
            requestPermissions(new String[]{Manifest.permission.CAMERA},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) {
            Log.d(TAG, "onRequestPermissionsResult: " + grantResults[0]);
            if (grantResults[0] == 0) {
                //已允许权限
                startCamera();
            }else if (grantResults[0] == -1) {
                //被禁止
                Toast.makeText(this,"获取相机权限失败,请重新进入或手动设置权                
                     限!",Toast.LENGTH_SHORT).show();
                finish();
            }
        }
}

2.4 第四步:使用PreviewView作为预览控件

<androidx.camera.view.PreviewView
    android:id="@+id/act_cameraTest_pv_cameraPreview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

2.5 第五步:开始预览

/**
 * 开始预览
 */
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));
}

3.实现拍照(只获取静态图片+拍照保存到本地)

官方提供了两个拍照方法,下面是源码中的代码

第一个是获取相机预览图像的静态图片

第二个是直接保存到文件,形成一个.jpg照片的,比上面多一个参数

 3.1只获取静态图片

/**
 * 获取静态图片
 */
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: ");
            }
        });
    }
}

3.2 拍照保存到本地

    /**
     * 拍照并存到存储空间
     * @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();
                }
            });
        }
    }

4.问题处理

4.1 Not bound to a valid Camera [ImageCapture:androidx.camera.core.ImageCapture-

问题现象:

调用拍照方法报错

原因:

没有绑定ImageCapture

解决方法:

在startCamera()方法中有一段绑定到Lifecycle的代码

 

 可以看到如果我把最后一个参数去掉代码不会报错也可以正常跑起来,因为这个方法源码是下图这样的,所以传参的时候传三个到多个都是可以的,如果报上面的错,应该是这里的ImageCapture参数没有传,加上之后可以解决这个问题。

4.2 CameraX 连续拍照两次后拍照回调方法一直无响应

问题现象:

连续调用两次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: ");
       }
});

需要源码的可以点个赞,点个关注,然后私信我。

以上是关于Android CameraX使用,预览拍照获取静态图像的主要内容,如果未能解决你的问题,请参考以下文章

Android 使用CameraX拍照预览

Android CameraX实现摄像头预览拍照录制视频

Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/缩放/切换摄像头等操作

Android的camerax预览拍照

Android的camerax预览拍照

使用CameraX几行代码实现摄像头预览和拍照