Open marclee44 opened 2 years ago
简介
CameraX是一个Jetpack支持库,旨在简化相机应用的开发工作。它提供一致且易用的API接口,适用于大多数Android设备,并可向后兼容至Android 5.0(API 级别 21)。 虽然CameraX利用了camera2的功能,但采取了一种具有生命周期感知能力且基于用例的更简单方式。它还解决了设备兼容性问题,因此无需在代码库中添加设备专属代码。这些功能减少了将相机功能添加到应用时需要编写的代码量。
基本使用
开发者使用CameraX,借助名为“用例”的抽象概念与设备的相机进行交互。目前提供的用例如下:
PreviewView
VideoCapture
不同用例可以组合使用,也可以同时处于活跃状态。例如,应用中可以加入预览用例,以便让用户查看进入相机视野的画面;加入图片分析用例,以确定照片里的人物是否在微笑;还可以加入图片拍摄用例,以在人物微笑时拍摄照片。
依赖项
当前CameraX的版本依然事将以下内容添加到使用的每个模块的build.gradle文件中:
build.gradle
dependencies { def camerax_version = "1.1.0-alpha06" // CameraX core library using camera2 implementation implementation "androidx.camera:camera-camera2:$camerax_version" // CameraX Lifecycle Library implementation "androidx.camera:camera-lifecycle:$camerax_version" // CameraX View class implementation "androidx.camera:camera-view:1.0.0-alpha26" }
权限
在AndroidManifest.xml中添加如下权限
AndroidManifest.xml
<uses-feature android:name="android.hardware.camera.any" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在使用摄像头的Activity/Fragment中,需要进行权限验证
private void checkPermissions() { ActivityResultLauncher<String[]> mRequestPermission = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> { if (result.values().stream().allMatch(p -> p)) { init(); } else { ToastUtils.showShort("有权限被拒绝,无法使用本功能。请在设置中打开所需权限。"); } }); if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { init(); } else { mRequestPermission.launch(new String[]{ Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }); } }
主要代码
UI层最简单,只要放一个PreviewView即可。当然,开始录像/停止录像/切换摄像头按钮不可少
<androidx.camera.view.PreviewView android:id="@+id/preview_video_recode" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/btn_video_recode_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="录制" /> <Button android:id="@+id/btn_video_recode_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止" /> <Button android:id="@+id/btn_switch_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换摄像头" /> </LinearLayout>
控制分几部分说
private PreviewView mPreviewView; private VideoCapture mVideoCapture; private ProcessCameraProvider mCameraProvider; private ListenableFuture<ProcessCameraProvider> mCameraFuture; private CameraSelector mCameraSelector; private Preview mPreview; private Button mStartBtn; private Button mStopBtn; private Button mSwitchBtn;
private void initEvent() { mStartBtn = findViewById(R.id.btn_video_recode_start); mStopBtn = findViewById(R.id.btn_video_recode_stop); mSwitchBtn = findViewById(R.id.btn_switch_camera); mStartBtn.setOnClickListener(v -> startRecorder()); mStopBtn.setOnClickListener(v -> stopRecorder()); mSwitchBtn.setOnClickListener(v -> switchCamera()); } @SuppressLint("RestrictedApi") private void initCamera() { mCameraFuture = ProcessCameraProvider.getInstance(requireContext()); mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; mPreview = new Preview.Builder() .build(); mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider()); mVideoCapture = new VideoCapture.Builder() .build(); try { mCameraProvider = mCameraFuture.get(); } catch (ExecutionException | InterruptedException e) { Logger.e("初始化摄像头失败,%s", e.toString()); } }
多个用例可以同时运行。虽然可以将多个用例依序绑定到一个生命周期,但最好通过对CameraProcessProvider.bindToLifecycle()的一次调用来绑定所有用例。
CameraProcessProvider.bindToLifecycle()
private void startPreview() { mCameraFuture.addListener(() -> { try { mCameraProvider.unbindAll(); mCameraProvider.bindToLifecycle(getViewLifecycleOwner(), mCameraSelector, mPreview, mVideoCapture); } catch (Exception e) { Logger.e("预览摄像头失败,%s", e.toString()); } }, ContextCompat.getMainExecutor(requireContext())); }
在预览状态(未在录像中),可以切换前后摄像头。此时,需要重新绑定所有用例。
private void switchCamera() { mCameraSelector = mCameraSelector == CameraSelector.DEFAULT_BACK_CAMERA ? CameraSelector.DEFAULT_FRONT_CAMERA : CameraSelector.DEFAULT_BACK_CAMERA; startPreview(); }
停止录像/录像异常的回调,在开始录像时进行设置
@SuppressLint("RestrictedApi") private void startRecorder() { String dirPath = requireContext().getExternalFilesDir("").getAbsolutePath(); File dirFile = new File(dirPath); if (!dirFile.exists()) { boolean mkdir = dirFile.mkdir(); } File file = new File(dirFile, System.currentTimeMillis() + ".mp4"); if (!file.exists()) { try { boolean newFile = file.createNewFile(); } catch (IOException e) { Logger.e("创建视频文件失败,%s", e.toString()); } } if (ActivityCompat.checkSelfPermission(this.requireContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { return; } mVideoCapture.startRecording(new VideoCapture.OutputFileOptions.Builder(file).build(), ContextCompat.getMainExecutor(requireContext()), new VideoCapture.OnVideoSavedCallback() { @Override public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) { Uri uri = outputFileResults.getSavedUri();、 ToastUtils.showShort(uri.getPath()+"录制完成。"); } @Override public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) { Logger.e("录制出错。code:%s,%s", videoCaptureError, message); } } ); }
@SuppressLint("RestrictedApi") private void stopRecorder() { if (mVideoCapture != null) { mVideoCapture.stopRecording(); } }
CameraX是一个Jetpack支持库,旨在简化相机应用的开发工作。它提供一致且易用的API接口,适用于大多数Android设备,并可向后兼容至Android 5.0(API 级别 21)。 虽然CameraX利用了camera2的功能,但采取了一种具有生命周期感知能力且基于用例的更简单方式。它还解决了设备兼容性问题,因此无需在代码库中添加设备专属代码。这些功能减少了将相机功能添加到应用时需要编写的代码量。
开发者使用CameraX,借助名为“用例”的抽象概念与设备的相机进行交互。目前提供的用例如下:
PreviewView
。VideoCapture
拍摄视频和音频不同用例可以组合使用,也可以同时处于活跃状态。例如,应用中可以加入预览用例,以便让用户查看进入相机视野的画面;加入图片分析用例,以确定照片里的人物是否在微笑;还可以加入图片拍摄用例,以在人物微笑时拍摄照片。
当前CameraX的版本依然事将以下内容添加到使用的每个模块的
build.gradle
文件中:在
AndroidManifest.xml
中添加如下权限在使用摄像头的Activity/Fragment中,需要进行权限验证
UI层最简单,只要放一个
PreviewView
即可。当然,开始录像/停止录像/切换摄像头按钮不可少控制分几部分说
多个用例可以同时运行。虽然可以将多个用例依序绑定到一个生命周期,但最好通过对
CameraProcessProvider.bindToLifecycle()
的一次调用来绑定所有用例。在预览状态(未在录像中),可以切换前后摄像头。此时,需要重新绑定所有用例。
停止录像/录像异常的回调,在开始录像时进行设置