CameraX 1.0.0-Bate02 使用

1,329 阅读1分钟

官方发布

本文

从官方示例 Copy 而来,主要代码来自 Google DemoCameraFragment

开始使用 CameraX

1. 导入依赖

在 app 或 module 的 build.gradle 中添加依赖:

dependencies {
    def camerax_version = '1.0.0-beta02'
    implementation "androidx.camera:camera-core:$camerax_version"
    implementation "androidx.camera:camera-camera2:$camerax_version"
    implementation "androidx.camera:camera-lifecycle:$camerax_version"
    implementation 'androidx.camera:camera-view:1.0.0-alpha09'
}

配置 Java 版本 8 以上

android {
    ...
    defaultConfig {
        // Jetpack 库向前兼容的最低版本 Android L
        minSdkVersion 21
        ...
    }
    
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

2. 添加相机权限

    <uses-permission android:name="android.permission.CAMERA" />

3. 添加 PreviewView

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

4. 请求相机权限(略)

5. 初始化相机,开启预览

  • 选择摄像头(前置 or 后置)
private int lensFacing = CameraSelector.LENS_FACING_BACK;

CameraSelector cameraSelector = new CameraSelector.Builder()
        .requireLensFacing(lensFacing)
        .build();
  • 初始化 UseCase (包括 PreView、ImageCapture、ImageAnalysis、VideoCapture)
private static final double RATIO_16_9 = 16.0 / 9.0;
private static final double RATIO_4_3 = 4.0 / 3.0;

private PreviewView previewView;

private Preview preview;
private ImageCapture imageCapture;

private void initUseCase() {
    // PreView 的比例和方向
    int ratio = getPreviewRatio();
    int rotation = previewView.getDisplay().getRotation();

    // 初始化相机预览
    preview = new Preview.Builder()
            .setTargetAspectRatio(ratio)
            .setTargetRotation(rotation)
            .build();

    // 初始化捕获照片
    imageCapture = new ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setTargetAspectRatio(ratio)
            .setTargetRotation(rotation)
            .build();
}

// 确定比例
private int getPreviewRatio() {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    previewView.getDisplay().getRealMetrics(displayMetrics);
    int width = displayMetrics.widthPixels;
    int height = displayMetrics.heightPixels;

    double previewRatio = ((double) Math.max(width, height)) / Math.min(width, height);
    if (Math.abs(previewRatio - RATIO_4_3) <= Math.abs(previewRatio - RATIO_16_9)) {
        return AspectRatio.RATIO_4_3;
    }
    return AspectRatio.RATIO_16_9;
}
  • 绑定相机
previewView = findViewById(R.id.preview_view);
previewView.post(this::bindCamera);

private void bindCamera() {
    // 选择摄像头
    CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(lensFacing)
                .build();
    
    // 获取 ProcessCameraProvider 
    ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    cameraProviderFuture.addListener(() -> {
        // 初始化 UseCase 
        initUseCase();
        try {
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
            cameraProvider.unbindAll();
            
            // 绑定 UseCase 到相机
            camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);

            // 开始预览
            preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo()));
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
    }, ContextCompat.getMainExecutor(this));
}

6. 触发捕获照片

takePhotoView.setOnClickListener(v -> {
    // 存储文件
    File imgFile = Utils.createPhotoFile(outputFile);
    
    // 存储原始数据,设置前置摄像头镜面配置
    ImageCapture.Metadata metadata = new ImageCapture.Metadata();
    metadata.setReversedHorizontal(lensFacing == CameraSelector.LENS_FACING_FRONT);
    
    // 配置输出
    ImageCapture.OutputFileOptions outputFileOptions =
            new ImageCapture.OutputFileOptions.Builder(imgFile)
                .setMetadata(metadata)
                .build();
                
    // 触发拍照
    if (imageCapture != null) {
        imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
                new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        // 此处处理所得照片
                        Uri uri = outputFileResults.getSavedUri();
                        resultImg.setVisibility(View.VISIBLE);
                        Glide.with(resultImg)
                                .load(uri)
                                .into(resultImg);
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        exception.printStackTrace();
                    }
                });
    }
});

至此便可以实现简单的相机使用,包括预览和拍照。


完整代码:github.com/hanjx-dut/C…

  • MainActivity
public class MainActivity extends AppCompatActivity {
    private static final double RATIO_16_9 = 16.0 / 9.0;
    private static final double RATIO_4_3 = 4.0 / 3.0;

    private PreviewView previewView;
    private View takePhotoView;
    private ImageView resultImg;

    private Preview preview;
    private Camera camera;
    private ImageCapture imageCapture;

    private int lensFacing = CameraSelector.LENS_FACING_BACK;
    private File outputFile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        outputFile = Utils.getOutputFile(this);

        previewView = findViewById(R.id.preview_view);
        takePhotoView = findViewById(R.id.take_photo_img);
        resultImg = findViewById(R.id.result_img);

        previewView.post(this::bindCamera);
        initListener();
    }

    private void initListener() {
        takePhotoView.setOnClickListener(v -> {
            File imgFile = Utils.createPhotoFile(outputFile);
            ImageCapture.Metadata metadata = new ImageCapture.Metadata();
            metadata.setReversedHorizontal(lensFacing == CameraSelector.LENS_FACING_FRONT);
            ImageCapture.OutputFileOptions outputFileOptions =
                    new ImageCapture.OutputFileOptions.Builder(imgFile)
                        .setMetadata(metadata)
                        .build();
            if (imageCapture != null) {
                imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
                        new ImageCapture.OnImageSavedCallback() {
                            @Override
                            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                                Uri uri = outputFileResults.getSavedUri();
                                resultImg.setVisibility(View.VISIBLE);
                                Glide.with(resultImg)
                                        .load(uri)
                                        .into(resultImg);
                            }

                            @Override
                            public void onError(@NonNull ImageCaptureException exception) {
                                exception.printStackTrace();
                            }
                        });
            }
        });

        resultImg.setOnClickListener(v -> resultImg.setVisibility(View.GONE));
    }

    private void bindCamera() {
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(lensFacing).build();
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(() -> {
            initUseCase();
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                cameraProvider.unbindAll();
                camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);

                preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo()));

            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private void initUseCase() {
        int ratio = getPreviewRatio();
        int rotation = previewView.getDisplay().getRotation();

        preview = new Preview.Builder()
                .setTargetAspectRatio(ratio)
                .setTargetRotation(rotation)
                .build();

        imageCapture = new ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .setTargetAspectRatio(ratio)
                .setTargetRotation(rotation)
                .build();
    }

    private int getPreviewRatio() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        previewView.getDisplay().getRealMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;

        double previewRatio = ((double) Math.max(width, height)) / Math.min(width, height);
        if (Math.abs(previewRatio - RATIO_4_3) <= Math.abs(previewRatio - RATIO_16_9)) {
            return AspectRatio.RATIO_4_3;
        }
        return AspectRatio.RATIO_16_9;
    }
}
  • Utils
class Utils {
    static File getOutputFile(Context context) {
        Context appContext = context.getApplicationContext();
        File[] mediaDirs = context.getExternalMediaDirs();
        if (mediaDirs != null && mediaDirs.length > 0 && mediaDirs[0] != null) {
            File appMediaDir = new File(mediaDirs[0], appContext.getResources().getString(R.string.app_name));
            appMediaDir.mkdirs();
            return appMediaDir;
        }
        return appContext.getFilesDir();
    }

    static File createPhotoFile(File outputFile) {
        return new File(outputFile, String.format("%s%s", System.currentTimeMillis(), ".png"));
    }
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

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

    <View
        android:id="@+id/action_area"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="#50000000" />

    <ImageView
        android:id="@+id/take_photo_img"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/take_photo"
        app:layout_constraintTop_toTopOf="@id/action_area"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ImageView
        android:id="@+id/result_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.example.cameraxdemo"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // CameraX core library
    def camerax_version = '1.0.0-beta02'
    implementation "androidx.camera:camera-core:$camerax_version"
    implementation "androidx.camera:camera-camera2:$camerax_version"
    implementation "androidx.camera:camera-lifecycle:$camerax_version"
    implementation 'androidx.camera:camera-view:1.0.0-alpha09'
    implementation 'com.github.bumptech.glide:glide:4.11.0'

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}