android CameraX构建相机拍照

Android CameraX 是一个 Jetpack 支持库,旨在简化相机应用的开发工作。它提供了一致且易用的API接口,适用于大多数Android设备,并可向后兼容至Android 5.0(API级别21)。

CameraX解决了在多种设备上实现相机功能时所遇到的兼容性问题,大大减少了需要编写的设备专属代码量。同时,它还通过提供一种基于用例的、具有生命周期感知能力的方式,简化了相机功能的开发。

导入需要的包

bash 复制代码
    implementation("androidx.camera:camera-core:1.3.4")
    implementation("androidx.camera:camera-camera2:1.3.4")
    implementation("androidx.camera:camera-lifecycle:1.3.4")
    implementation("androidx.camera:camera-video:1.3.4")
    implementation("androidx.camera:camera-view:1.3.4")

配置简单layout和PhotoActivity

新建一个activity_photo.xml:

bash 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:xhead="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#005dd7"
    android:orientation="vertical">
  <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_margin="20dp"
        android:background="@drawable/shape_common_dialog_bg"
        android:orientation="vertical">

    <androidx.camera.view.PreviewView
        android:layout_width="640dp"
        android:layout_height="600dp"
        android:id="@+id/viewFinder" />

    <Button
        android:layout_marginTop="10dp"
        android:background="@drawable/shape_login_button"
        android:id="@+id/image_capture_button"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:elevation="2dp"
        android:text="拍照" />
</LinearLayout>
java 复制代码
public class PhotoActivity implements View.OnClickListener {

	//图片捕获
   private ImageCapture imageCapture;
	//
    private ProcessCameraProvider cameraProvider;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo);
    }

请求必要的权限

应用需要获得用户授权才能打开相机。

首先在AndroidManifest.xml中声明
<uses-permission android:name="android.permission.CAMERA" />

所以在onCreate(@Nullable Bundle savedInstanceState)方法中增加:

java 复制代码
 protected void onCreate(@Nullable Bundle savedInstanceState){
 String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
        if (allPermissionsGranted(permissions, this)) {
            startCamera();
        } else {
            ActivityCompat.requestPermissions(this, permissions, REQUEST_CAMERA_PERMISSION);
        }
}
        
        public boolean allPermissionsGranted(String[] permissions, Context context) {
        for (String permission : permissions) {
            int checkSelfPermission = ContextCompat.checkSelfPermission(context, permission);
            if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, permissions, REQUEST_CAMERA_PERMISSION);
            }
        }
        return true;
    }

当然用户存在不同意的情况,此时可以重写 onRequestPermissionsResult 方法以处理权限请求结果:

java 复制代码
 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限已被授予,可以执行相机相关操作
                startCamera();
            } else {
                // 权限被拒绝,提示用户需要授权
                Toast.makeText(this, "需要相机权限才能使用此功能", Toast.LENGTH_SHORT).show();
            }
        }
    }

实现 Preview 用例,开始拍照

我们打开前置摄像头:

java 复制代码
 private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

        cameraProviderFuture.addListener(() -> {
            try {
                cameraProvider = cameraProviderFuture.get();
                //前置摄像头
                CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
                Preview preview = new Preview.Builder().build();

                // 构建预览用例
                preview.setSurfaceProvider(bd.viewFinder.getSurfaceProvider());

                // 构建图片捕获用例
                imageCapture = new ImageCapture.Builder().build();

                // 将用例绑定到相机
                cameraProvider.bindToLifecycle(
                        PersonPhotoActivity.this,
                        cameraSelector,
                        preview,
                        imageCapture);
            } catch (ExecutionException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, ContextCompat.getMainExecutor(this));

    }
  • ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this); 这行代码的作用是获取一个ProcessCameraProvider的实例,这个实例可以与宿主的生命周期绑定,从而无需手动管理相机的打开和关闭。
  • cameraProviderFuture注册了一个监听器,当cameraProviderFuture完成时,会执行注册的Runnable。在这个Runnable中,首先通过cameraProviderFuture.get()获取到cameraProvider实例。然后,创建一个用于预览的Preview对象,并将其Surface设置到对应的预览界面上。同时,还创建了一个ImageCapture对象用于图片捕获。
  • 使用cameraProvider.bindToLifecycle()方法将相机与当前Activity的生命周期绑定,并指定使用的摄像头、预览、以及图片捕获的配置。这样,相机就会在Activity的生命周期内自动进行资源的申请和释放

在avd虚拟机中显示

配置avd的前置摄像头为虚拟模式:

启动项目查看页面:

拍照

当拍照按钮被点击时调用takePhoto 方法,这里我们建立了一个file对象临时保存当前照片,之后我们就可以自由使用该照片:

java 复制代码
 @Override
    public void onClick(View view) {
        if (view.getId() == R.id.image_capture_button) {
            takePhoto(view);
        }
    }
java 复制代码
 private void takePhoto(View view) {
        File file = new File(getContext().getExternalCacheDir() + File.separator + System.currentTimeMillis() + ".png");
        //创建包文件的数据,比如创建文件
        ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
        //开始拍照
        imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
                new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        // 关闭相机并释放资源
                        cameraProvider.unbindAll();
                        // file.getPath()
						//做业务
						//。。。。
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Log.d(TAG, Objects.requireNonNull(exception.toString()));
                        Toast.makeText(PersonPhotoActivity.this, "照片保存失败", Toast.LENGTH_SHORT).show();
                    }
                });
    }

代码分析:

  1. 创建一个ImageCapture.OutputFileOptions对象,用于指定照片的输出选项,这里将照片保存到刚刚创建的File对象中。
  2. 调用imageCapture的takePicture方法开始拍照。传入outputFileOptions作为参数,以及一个回调函数OnImageSavedCallback。
  3. 在OnImageSavedCallback的onImageSaved方法中,当照片成功保存后,关闭相机并释放资源。然后可以进行其他业务操作,如显示照片等。
相关推荐
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿3 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神5 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛5 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法5 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter6 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快8 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl8 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江8 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
Dnelic-8 小时前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记