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方法中,当照片成功保存后,关闭相机并释放资源。然后可以进行其他业务操作,如显示照片等。
相关推荐
Albert Edison8 小时前
【Python】文件
android·服务器·python
大模型玩家七七9 小时前
效果评估:如何判断一个祝福 AI 是否“走心”
android·java·开发语言·网络·人工智能·batch
Aurora4199 小时前
Android事件分发逻辑--针对事件分发相关函数的讲解
android
AI视觉网奇9 小时前
3d 数字人 ue 手大问题解决 相机视角设置
数码相机·3d
似霰9 小时前
Android 日志系统4——logd 写日志过程分析一
android
youyoulg10 小时前
利用Android Studio编译Android上可直接执行的二进制
android·ide·android studio
闽农10 小时前
Android ANR 调用栈溯源
android·anr
似霰10 小时前
Android 日志系统7——Android 平台日志丢失问题分析
android·log
·云扬·10 小时前
MySQL Undo Log 深度解析:事务回滚与 MVCC 的底层支柱
android·数据库·mysql