Android开发笔记(一百八十一)使用CameraX拍照

Posted aqi00

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发笔记(一百八十一)使用CameraX拍照相关的知识,希望对你有一定的参考价值。

常言道,眼睛是心灵的窗户,那么相机便是手机的窗户了,主打美颜相机功能的拍照手机大行其道,可见对于手机App来说,如何恰如其分地运用相机开发至关重要。
android的SDK一开始就自带了相机工具Camera,从Android5.0开始又推出了升级版的camera2,然而不管是初代的Camera还是二代的camera2,编码过程都比较繁琐,对于新手而言有点艰深。为此谷歌公司在Jetpack库中集成了增强的相机库CameraX,想让相机编码(包括拍照和录像)变得更加方便。CameraX基于camera2开发,它提供一致且易用的API接口,还解决了设备兼容性问题,从而减少了编码工作量。
不管是拍照还是录像,都要在AndroidManifest.xml中添加相机权限,还要添加存储卡访问权限,如下所示:

<!-- 相机 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 存储卡读写 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

由于CameraX来自Jetpack库,因此要修改模块的build.gradle,往dependencies节点添加以下几配置,表示导入指定版本的camerax库:

// camerax库各版本见 https://mvnrepository.com/artifact/androidx.camera/camera-core
implementation 'androidx.camera:camera-core:1.0.1'
// camerax库各版本见 https://mvnrepository.com/artifact/androidx.camera/camera-camera2
implementation 'androidx.camera:camera-camera2:1.0.1'
// camerax库各版本见 https://mvnrepository.com/artifact/androidx.camera/camera-lifecycle
implementation 'androidx.camera:camera-lifecycle:1.0.1'
// camerax库各版本见 https://mvnrepository.com/artifact/androidx.camera/camera-view
implementation 'androidx.camera:camera-view:1.0.0-alpha28'

使用CameraX拍照之前要先初始化相机,包括界面预览以及参数设定等等,具体的初始化步骤说明如下。
1、准备一个预览视图对象PreviewView,并添加至当前界面;
2、获取相机提供器对象ProcessCameraProvider;
3、构建预览对象Preview,指定预览的宽高比例;
4、构建摄像头选择器对象CameraSelector,指定使用前置摄像头还是后置摄像头;
5、构建图像捕捉器对象ImageCapture,分别设置捕捉模式、旋转角度、宽高比例、闪光模式等拍照参数;
6、调用相机提供器对象的bindToLifecycle方法,把相机选择器、预览视图、图像捕捉器绑定到相机提供器;
7、调用预览视图对象的setSurfaceProvider方法,设置预览视图的表面提供器;
把上述的初始化步骤串起来,写到一个自定义的相机视图控件中,形成了以下的CameraX初始化代码:

private Context mContext; // 声明一个上下文对象
private PreviewView mCameraPreview; // 声明一个预览视图对象
private CameraSelector mCameraSelector; // 声明一个摄像头选择器
private Preview mPreview; // 声明一个预览对象
private ProcessCameraProvider mCameraProvider; // 声明一个相机提供器
private ImageCapture mImageCapture; // 声明一个图像捕捉器
private VideoCapture mVideoCapture; // 声明一个视频捕捉器
private ExecutorService mExecutorService; // 声明一个线程池对象
private LifecycleOwner mOwner; // 声明一个生命周期拥有者
private int mCameraType = CameraSelector.LENS_FACING_BACK; // 摄像头类型
private int mAspectRatio = AspectRatio.RATIO_16_9; // 宽高比例
private int mFlashMode = ImageCapture.FLASH_MODE_AUTO; // 闪光灯模式
private String mMediaDir; // 媒体保存目录

public CameraXView(Context context, AttributeSet attrs) 
    super(context, attrs);
    mContext = context;
    mCameraPreview = new PreviewView(mContext); // 创建一个预览视图
    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    mCameraPreview.setLayoutParams(params);
    addView(mCameraPreview); // 把预览视图添加到界面上
    mExecutorService = Executors.newSingleThreadExecutor(); // 创建一个单线程线程池
    mMediaDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();


// 打开相机
public void openCamera(LifecycleOwner owner, int cameraMode, OnStopListener sl) 
    mOwner = owner;
    mCameraMode = cameraMode;
    mStopListener = sl;
    mHandler.post(() ->  initCamera()); // 初始化相机


// 初始化相机
private void initCamera() 
    ListenableFuture future = ProcessCameraProvider.getInstance(mContext);
    future.addListener(() -> 
        try 
            mCameraProvider = (ProcessCameraProvider) future.get();
            resetCamera(); // 重置相机
         catch (Exception e) 
            e.printStackTrace();
        
    , ContextCompat.getMainExecutor(mContext));


// 重置相机
private void resetCamera() 
    int rotation = mCameraPreview.getDisplay().getRotation();
    // 构建一个摄像头选择器
    mCameraSelector = new CameraSelector.Builder().requireLensFacing(mCameraType).build();
    // 构建一个预览对象
    mPreview = new Preview.Builder()
            .setTargetAspectRatio(mAspectRatio) // 设置宽高比例
            .build();
    // 构建一个图像捕捉器
    mImageCapture = new ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) // 设置捕捉模式
            .setTargetRotation(rotation) // 设置旋转角度
            .setTargetAspectRatio(mAspectRatio) // 设置宽高比例
            .setFlashMode(mFlashMode) // 设置闪光模式
            .build();
    bindCamera(MODE_PHOTO); // 绑定摄像头
    // 设置预览视图的表面提供器
    mPreview.setSurfaceProvider(mCameraPreview.getSurfaceProvider());


// 绑定摄像头
private void bindCamera(int captureMode) 
    mCameraProvider.unbindAll(); // 重新绑定前要先解绑
    try 
        if (captureMode == MODE_PHOTO)  // 拍照
            // 把相机选择器、预览视图、图像捕捉器绑定到相机提供器的生命周期
            Camera camera = mCameraProvider.bindToLifecycle(
                    mOwner, mCameraSelector, mPreview, mImageCapture);
        
     catch (Exception e) 
        e.printStackTrace();
    


// 关闭相机
public void closeCamera() 
    mCameraProvider.unbindAll(); // 解绑相机提供器
    mExecutorService.shutdown(); // 关闭线程池

初始化相机之后,即可调用图像捕捉器的takePicture方法拍摄照片了,拍照代码示例如下:

private String mPhotoPath; // 照片保存路径
// 获取照片的保存路径
public String getPhotoPath() 
    return mPhotoPath;


// 开始拍照
public void takePicture() 
    mPhotoPath = String.format("%s/%s.jpg", mMediaDir, DateUtil.getNowDateTime());
    ImageCapture.Metadata metadata = new ImageCapture.Metadata();
    // 构建图像捕捉器的输出选项
    ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(new File(mPhotoPath))
            .setMetadata(metadata).build();
    // 执行拍照动作
    mImageCapture.takePicture(options, mExecutorService, new ImageCapture.OnImageSavedCallback() 
        @Override
        public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) 
            mStopListener.onStop("已完成拍摄,照片保存路径为"+mPhotoPath);
        

        @Override
        public void onError(ImageCaptureException exception) 
            mStopListener.onStop("拍摄失败,错误信息为:"+exception.getMessage());
        
    );

然后在App代码中集成新定义的增强相机控件,先在布局文件中添加CameraXView节点,如下所示。

    <com.example.chapter14.widget.CameraXView
        android:id="@+id/cxv_preview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

再给Java代码补充CameraXView对象的初始化以及拍照动作,其中关键代码示例如下:

private CameraXView cxv_preview; // 声明一个增强相机视图对象
private View v_black; // 声明一个视图对象
private ImageView iv_photo; // 声明一个图像视图对象
private final Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象

// 初始化相机
private void initCamera() 
    // 打开增强相机,并指定停止拍照监听器
    cxv_preview.openCamera(this, CameraXView.MODE_PHOTO, (result) -> 
        runOnUiThread(() -> 
            iv_photo.setEnabled(true);
            Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
        );
    );


// 处理拍照动作
private void dealPhoto() 
    iv_photo.setEnabled(false);
    v_black.setVisibility(View.VISIBLE);
    cxv_preview.takePicture(); // 拍摄照片
    mHandler.postDelayed(() -> v_black.setVisibility(View.GONE), 500);

运行测试App,点击拍照图标,观察到增强相机的拍照效果如下面两图所示,其中第一张图为准备拍照时的预览界面,第二张图为拍照结束后的观赏界面。

 

 


点此查看Android开发笔记的完整目录

以上是关于Android开发笔记(一百八十一)使用CameraX拍照的主要内容,如果未能解决你的问题,请参考以下文章

Android开发笔记(一百八十一)使用CameraX拍照

Android开发笔记(一百八十二)使用CameraX录像

Android开发笔记(一百八十二)使用CameraX录像

Android开发笔记(一百八十八)工作管理器WorkManager

Android开发笔记(一百八十八)工作管理器WorkManager

Android开发笔记(一百八十八)工作管理器WorkManager