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();
}
});
}
代码分析:
- 创建一个ImageCapture.OutputFileOptions对象,用于指定照片的输出选项,这里将照片保存到刚刚创建的File对象中。
- 调用imageCapture的takePicture方法开始拍照。传入outputFileOptions作为参数,以及一个回调函数OnImageSavedCallback。
- 在OnImageSavedCallback的onImageSaved方法中,当照片成功保存后,关闭相机并释放资源。然后可以进行其他业务操作,如显示照片等。