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

      }

});

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

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容