前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(一百八十一)使用CameraX拍照

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

作者头像
aqi00
发布2022-03-09 14:04:42
1.4K0
发布2022-03-09 14:04:42
举报
文章被收录于专栏:老欧说安卓老欧说安卓

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

代码语言:javascript
复制
<!-- 相机 -->
<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库:

代码语言:javascript
复制
// 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初始化代码:

代码语言:javascript
复制
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方法拍摄照片了,拍照代码示例如下:

代码语言:javascript
复制
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节点,如下所示。

代码语言:javascript
复制
    <com.example.chapter14.widget.CameraXView
        android:id="@+id/cxv_preview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

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

代码语言:javascript
复制
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开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档