前面讲了,camera1和camera2的简单使用。从前文可以看出来,API 的调用还是比较繁琐的,而且像一些最佳尺寸,角度等还得自己算。由于camera1支持的特性很少,camera2流程又很繁琐,google 也意识到这个问题,所以推出了 Camerax。它虽然底层也是利用了 Camera2 的功能,但它的使用却更加简洁,它的主要优势如下:
注意,Camerax 也只支持 API21 及以上。
本文也仅展示怎么预览和拍照,其他功能可以参照Android开发者官网。代码非常简单,如下:
首先,添加关联库:
- def camerax_version = "1.0.0-beta06"
- // CameraX 核心库
- implementation "androidx.camera:camera-camera2:$camerax_version"
- // CameraX 生命周期
- implementation "androidx.camera:camera-lifecycle:$camerax_version"
- // CameraX view 集合,比如 cameraview,preview等
- implementation "androidx.camera:camera-view:1.0.0-alpha10"
权限
- <uses-permission android:name="android.permission.CAMERA" />
- <uses-feature android:name="android.hardware.camera.any" />
预览
xml布局:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".FullScreenClipActivity">
- <androidx.camera.view.PreviewView
- android:id="@+id/pv"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- RelativeLayout>
通过
ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this);
返回当前可以绑定生命周期的 ProcessCameraProvider,ProcessCameraProvider 它会和宿主绑定生命周期,这样就不用担心打开相机和关闭的问题了。
接着,向 cameraProviderFuture 注册一个监听,第一个参数是一个 runnable,第二个参数是线程池,即runnable 运行在哪个线程中:
cameraProviderFuture.addListener(Runnable {}, ContextCompat.getMainExecutor(this))
上面我们让它运行在主线程的线程池中,这样,在这个 runnable 中,就可以配置我们的 capture 了,代码如下:
- mViewFinder = findViewById(R.id.pv);
- //创建相机进程监听
- ListenableFuture
cameraProviderFuture = ProcessCameraProvider.getInstance(this); - cameraProviderFuture.addListener(new Runnable() {
- @SuppressLint("RestrictedApi")
- @Override
- public void run() {
- try {
- //将相机的生命周期和activity的生命周期绑定,camerax 会自己释放,不用担心了
- ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
- //预览的 capture,它里面支持角度换算
- Preview preview = new Preview.Builder().build();
-
- //创建图片的 capture
- mImageCapture = new ImageCapture.Builder()
- .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
- .build();
-
-
- //选择后置摄像头
- CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
-
- //预览之前先解绑
- cameraProvider.unbindAll();
-
- //将数据绑定到相机的生命周期中
- Camera camera = cameraProvider.bindToLifecycle(CameraxActivity.this, cameraSelector, preview, mImageCapture);
- //将previewview 的 surface 给相机预览
- preview.setSurfaceProvider(mViewFinder.createSurfaceProvider(camera.getCameraInfo()));
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- },ContextCompat.getMainExecutor(this));
创建并确认 CameraProvider 后,执行步骤如下
拍照
上面已经设置了 mImageCapture,拍照在 Camerax 中也很简单,使用 takePicture 即可,如下
- public void takePhoto(View view) {
- if (mImageCapture != null) {
- File dir = new File(Constants.PATH);
- if (!dir.exists()) {
- dir.mkdirs();
- }
- //创建文件
- File file = new File(Constants.PATH,"testx.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(CameraxActivity.this, "保存成功: ", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onError(@NonNull ImageCaptureException exception) {
- Toast.makeText(CameraxActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
注释已经很清楚了,注意我们可以使用 ImageCapture.OutputFileOptions 让 Camerax 去帮我们保存图片文件。