【Android】Use CameraX to implement basic camera functions

Table of contents

1. Basic development environment

2. Add related dependencies

3. APP layout

4. Main process logic

5. Debug or install APK

6. Complete project code


1. Basic development environment

JDK:JDK17

Android Studio:Android Studio Giraffe | 2022.3.1

Android SDK:Android API 34

Gradle: gradle-7.2-bin.zip

CameraX Version: 1.1.0-alpha05

2. Add related dependencies

Add CameraX related dependencies in build.gradle

    // *** Camera 相关依赖 ***
    def cameraxVersion = "1.1.0-alpha05";
    implementation "androidx.camera:camera-core:${cameraxVersion}"
    implementation "androidx.camera:camera-camera2:${cameraxVersion}"
    implementation "androidx.camera:camera-lifecycle:${cameraxVersion}"

    implementation 'androidx.camera:camera-view:1.0.0-alpha25'
    // ***********************

Register camera permissions in AndroidManifest.xml file

    <!--  这个权限声明意味着此 Android 应用程序需要设备具有摄像头功能才能正常运行,
          并且如果设备没有摄像头,则应用程序将无法在该设备上安装或运行。
          "android:required = "true"" 表示摄像头功能是必需的,而不是可选的。 -->
    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />
    <!--  注册相机权限  -->
    <!--  这个权限允许应用程序读取摄像头的输入并拍照或录制视频。如果没有这个权限,应用程序将无法访问设备的相机功能。  -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!--  这个权限允许应用程序录制音频。  -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!--  这个权限允许应用程序向外部存储(例如SD卡)写入数据。而这个权限只适用于 Android 版本号不大于 28 的设备。  -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

3. APP layout

Use the LinearLayout layout, add a PreviewView to display the preview of the camera screen, and add a Button to control the photo taking.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@color/black"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="3" />

    <Button
        android:id="@+id/captureButton"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_weight="1"
        android:text="@string/capture_button" />

</LinearLayout>

<resources>
    <string name="app_name">camera-image-capture</string>
    <string name="capture_button">拍照</string>
</resources>

4. Main process logic

package com.example.capture;

import android.content.ContentValues;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ListenableFuture<ProcessCameraProvider> processCameraProviderListenableFuture;
    PreviewView previewView;
    Button captureButton;
    private ImageCapture imageCapture;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // onCreate 在活动被创建时被调用的。
        // 它的作用是对活动进行初始化,例如加载布局文件,设置事件监听器和初始化变量等。
        // `Bundle savedInstanceState` 参数用于保存活动状态,以便在活动被销毁后能够恢复它的状态。
        super.onCreate(savedInstanceState);
        // 将指定的布局文件加载到当前 Activity 中并显示在屏幕上。
        setContentView(R.layout.activity_main);

        // 从当前布局中查找具有指定 ID 的视图,并将其返回为 Java 对象。
        previewView = findViewById(R.id.previewView);
        captureButton = findViewById(R.id.captureButton);

        // 将当前类实现的 OnClickListener 接口设置为 captureButton 的点击事件监听器,
        // 以便在单击 captureButton 时调用类中的 onClick() 方法来处理点击事件。
        captureButton.setOnClickListener(this);

        // 这行代码的作用是获取相机提供者的实例,它是使用 Android CameraX API 实现相机功能的关键对象之一,
        // 通过它可以获取相机设备、预览用例、图像分析用例等等,从而实现相机应用的各种功能。
        // 此代码返回一个ListenableFuture对象,用于异步获取相机提供者的实例。
        processCameraProviderListenableFuture = ProcessCameraProvider.getInstance(this);
        // 监听摄像头的准备情况。准备好时,该代码块中的 start() 方法将被调用,以便启动相机。
        processCameraProviderListenableFuture.addListener(() -> {
            try {
                ProcessCameraProvider processCameraProvider = processCameraProviderListenableFuture.get();
                start(processCameraProvider);
            } catch (ExecutionException | InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 将监听器绑定到主线程,以确保在 UI 上下文中运行该代码块。
        }, ContextCompat.getMainExecutor(this));

    }

    private void start(ProcessCameraProvider processCameraProvider) {
        // 取消当前已经绑定的摄像头设备,释放它们的资源,以便其他应用或者进程可以使用这些摄像头设备。
        // 这个方法通常在摄像头应用程序退出或者暂停时调用,以确保摄像头设备不会一直占用系统资源。
        processCameraProvider.unbindAll();

        // 创建一个相机选择器对象,并指定选择前置摄像头。
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                .build();

        // 创建一个相机预览对象
        // 并将其与一个 SurfaceView 组件(previewView)的 SurfaceProvider 绑定,从而在该组件上显示相机预览画面。
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        // 创建一个 ImageCapture 对象,用于拍摄照片。
        // 设置拍照模式为最小化延迟模式,这意味着拍照时将尽可能快地捕获图像。
        imageCapture = new ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();

        // 在 Android 设备上启动相机,并将其与当前生命周期绑定,以便在应用程序暂停或停止时释放相机资源。
        // 该方法接受一个 `CameraSelector` 对象用于选择相机设备,一个 `Preview` 对象用于显示预览,以及一个 `ImageCapture` 对象用于捕获图像。
        processCameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
    }

    @Override
    public void onClick(View view) {
        // onClick(View view) 的作用是为按钮或其他视图设置点击事件处理程序。
        // 当用户点击该视图时,该方法会被调用并执行其中的代码。
        if (view.getId() == R.id.captureButton) {
            captureImage();
        }
    }

    private void captureImage() {
        long timeStamp = System.currentTimeMillis();
        // 通过 ContentValues 对象设置文件名和文件类型
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, timeStamp);
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");

        imageCapture.takePicture(
                // 第一个参数 OutputFileOptions 指定了照片保存的位置和格式等信息。
                new ImageCapture.OutputFileOptions.Builder(
                        getContentResolver(),
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        contentValues
                ).build(),
                // 第二个参数 Executor 指定了保存照片时要运行的线程。
                ContextCompat.getMainExecutor(this),
                // 第三个参数 OnImageSavedCallback 指定了保存照片完成后的回调函数,可以在其中进行一些提示或其他操作。
                new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        Toast.makeText(MainActivity.this, "Saving...", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Toast.makeText(MainActivity.this, "Error: " + exception.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                });
    }
}

5. Debug or install APK

Use USB debugging or Build to export the APK (Build -> Make Project) and then find the app-debug.apk file to install.

Note: Since there is no permission application part in the code logic, you need to manually enable the photo permission after installation.

The compiled APK file can be found at the beginning of the article and can be downloaded and installed directly.

6. Complete project code

https://gitee.com/hl0929/camera-image-capture

Guess you like

Origin blog.csdn.net/u014147522/article/details/132042521