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

2021-09-05  本文已影响0人  浮世绘町

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: ");

      }

});

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

上一篇下一篇

猜你喜欢

热点阅读