【Android】一步步实现手机拍照、录像及存储至相册(CameraX)

这篇具有很好参考价值的文章主要介绍了【Android】一步步实现手机拍照、录像及存储至相册(CameraX)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考资料

  • https://developer.android.google.cn/codelabs/camerax-getting-started?hl=zh-cn#0
  • https://developer.android.google.cn/training/camerax/video-capture?hl=zh-cn
  • //https://blog.csdn.net/teolih/article/details/120423971

对应视频及项目代码

【项目代码】https://gitee.com/hellosunshine/camerax_android_java.git

步骤(java版本)

基本功能
  • 预览界面
  • 拍摄图片
  • 录制视频
  • 分析图片
创建项目

android 拍照后保存图片,Android基础,android
android 拍照后保存图片,Android基础,android

创建活动

android 拍照后保存图片,Android基础,android
android 拍照后保存图片,Android基础,android
启动

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
CameraX、LifeCycle添加依赖
dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    //CameraX添加依赖
    def camerax_version = "1.1.0-alpha08"
    def lifecycle_version = "2.5.1"
    implementation "androidx.camera:camera-camera2:$camerax_version"
    implementation "androidx.camera:camera-lifecycle:$camerax_version"
    implementation "androidx.camera:camera-view:1.3.0-alpha07"
    implementation 'androidx.camera:camera-video:'
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
}
布局添加
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

    <Button
        android:id="@+id/takeVideoBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/startRecord"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/takePhotoBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias=".9" />

    <Button
        android:id="@+id/takePhotoBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/takePhoto"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/takeVideoBtn"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias=".9" />
</androidx.constraintlayout.widget.ConstraintLayout>
添加相机、读写存储权限
    <!-- 拍照权限 -->
    <uses-feature android:name="android.hardware.camera.any" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
绑定控件 & 添加监听 & 获取权限
package com.example.cameraxjavaactivity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    //按钮
    Button takePhotoButton;
    Button takeVideoButton;
    //预览
    PreviewView previewView;
    //权限
    private static final String[] REQUIRE_PERMISSIONS = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
    public static final int REQUEST_CODE_PERMISSIONS = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //绑定控件
        takePhotoButton = findViewById(R.id.takePhotoBtn);
        takeVideoButton = findViewById(R.id.takeVideoBtn);
        previewView = findViewById(R.id.preview_view);
        takePhotoButton.setOnClickListener(v -> takePhoto());
        takeVideoButton.setOnClickListener(v -> takeVideo());
        //获取权限
        if (havePermissions()) {
            initCamera();
        } else {
            ActivityCompat.requestPermissions(this, REQUIRE_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == REQUEST_CODE_PERMISSIONS) {
            initCamera();
        } else {
            finish();
        }
    }

    //判断权限是否获取
    private boolean havePermissions() {
        for (String permission : REQUIRE_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    //初始化Camera
    private void initCamera() {

    }

    //拍照
    private void takePhoto() {

    }

    //录像
    private void takeVideo() {

    }
}

运行没有报错,继续下步骤

初始化相机
实现拍照功能
  • 定义imageCapture和processCameraProviderListenableFuture
//capture
    ImageCapture imageCapture;
    ListenableFuture<ProcessCameraProvider> processCameraProviderListenableFuture;
  • 初始化相机预览
    //初始化Camera
    private void initCamera() {
        ///实例化(可以设置许多属性)
        imageCapture = new ImageCapture.Builder().build();
        processCameraProviderListenableFuture = ProcessCameraProvider.getInstance(this);
        processCameraProviderListenableFuture.addListener(() -> {
            try {
                //配置预览(https://developer.android.google.cn/training/camerax/preview?hl=zh-cn)
                previewView.setScaleType(PreviewView.ScaleType.FIT_CENTER);
                Preview preview = new Preview.Builder().build();
                preview.setSurfaceProvider(previewView.getSurfaceProvider());
                //绑定到生命周期
                ProcessCameraProvider processCameraProvider = processCameraProviderListenableFuture.get();
                processCameraProvider.unbindAll();
                processCameraProvider.bindToLifecycle(MainActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }
  • 运行代码,看到预览界面有了,继续下步骤
  • 实现按下拍照,并存储至相册
//拍照
   private void takePhoto() {
       if (imageCapture != null) {
           //ContentValues
           String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.SIMPLIFIED_CHINESE).format(System.currentTimeMillis())
           ContentValues contentValues = new ContentValues();
           contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
           contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
               contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/CameraXImage");
           }
           //图片输出
           ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions
                   .Builder(getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
                   .build();
           imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
               @Override
               public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                   Log.i("CameraXTest", Objects.requireNonNull(outputFileResults.getSavedUri()).toString());
               }

               @Override
               public void onError(@NonNull ImageCaptureException exception) {
                   Log.e("CameraXTest", exception.toString());
               }
           });
       }
   }
  • 运行后点击拍照可以看到照片被保存到手机相册
实现录像功能

定义videoCapture、recording

   VideoCapture videoCapture;
   Recording recording;
  • 构建实例
	Recorder recorder = new Recorder.Builder().build();
    videoCapture = VideoCapture.withOutput(recorder);

添加videoCapture

processCameraProvider.bindToLifecycle(MainActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture, videoCapture);
//processCameraProvider.bindToLifecycle(MainActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture);
  • 录像
//录像
   private void takeVideo() {
       if (videoCapture != null) {
           takeVideoButton.setEnabled(false);
           if (recording != null) {
               recording.stop();
               recording = null;
               return;
           }
           String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.SIMPLIFIED_CHINESE).format(System.currentTimeMillis()) + ".mp4";
           ContentValues contentValues = new ContentValues();
           contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
           contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
               contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "Movies/CameraX-Video");
           }
           MediaStoreOutputOptions mediaStoreOutputOptions = new MediaStoreOutputOptions
                   .Builder(getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                   .setContentValues(contentValues)
                   .build();
           Recorder recorder = (Recorder) videoCapture.getOutput();
           if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
               ActivityCompat.requestPermissions(this, REQUIRE_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
               return;
           }
           recording = recorder.prepareRecording(this, mediaStoreOutputOptions)
                   .withAudioEnabled()
                   .start(ContextCompat.getMainExecutor(this), videoRecordEvent -> {
                       if (videoRecordEvent instanceof VideoRecordEvent.Start) {
                           takeVideoButton.setText(getString(R.string.stopRecord));
                           takeVideoButton.setEnabled(true);
                       } else if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
                           if (((VideoRecordEvent.Finalize) videoRecordEvent).hasError()) {
                               if (recording != null) {
                                   recording.close();
                                   recording = null;
                               }
                           } else {
                               String msg = "视频为" + ((VideoRecordEvent.Finalize) videoRecordEvent).getOutputResults().getOutputUri();
                               Log.i("CameraXTest", msg);
                           }
                           takeVideoButton.setEnabled(true);
                           takeVideoButton.setText(getString(R.string.startRecord));
                       }
                   });
       }
   }
  • 点击录像后录制,录制后点击停止录像,相册会出现一段视频
    android 拍照后保存图片,Android基础,android
    android 拍照后保存图片,Android基础,android
    android 拍照后保存图片,Android基础,android
相机扩展

https://developer.android.google.cn/training/camerax/extensions-api?hl=zh-cn
本人设备不支持文章来源地址https://www.toymoban.com/news/detail-728284.html

图片分析
  • 添加imageAnalysis
    //executor & imageAnalysis
    private ExecutorService cameraExecutor;
    private ImageAnalysis imageAnalysis;
  • 实例化
    在绑定生命周期前面执行
    //图片分析
   @SuppressLint("UnsafeOptInUsageError")
   private void initImageAnalysis() {
       imageAnalysis = new ImageAnalysis.Builder()
               .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
               .build();
       imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
           int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
           Log.i("CameraXTest", String.valueOf(Objects.requireNonNull(imageProxy.getImage()).getTimestamp()));
           Log.i("CameraXTest", String.valueOf(rotationDegrees));
           imageProxy.close();
       });
   }
  • 修改绑定生命周期
processCameraProvider.bindToLifecycle(MainActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, imageAnalysis, preview, imageCapture, videoCapture);
  • 运行,查看日志
其他配置
  • 闪光灯模式
imageCapture = new ImageCapture.Builder()
               //控制闪光灯模式 FLASH_MODE_ON(拍照时,闪光灯会亮起)
               .setFlashMode(ImageCapture.FLASH_MODE_ON)
               .build();
  • 旋转(屏幕方向与最后拍出的图片方向)
///旋转
   //orientation为北为0,顺时针度数0-360
   //Surface.ROTATION_270将拍摄好的图片顺时针旋转270度
   private void setOrientationEventListener() {
       OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
           @Override
           public void onOrientationChanged(int orientation) {
               int rotation;
               if (orientation >= 45 && orientation < 135) {
                   rotation = Surface.ROTATION_270;
               } else if (orientation >= 135 && orientation < 225) {
                   rotation = Surface.ROTATION_180;
               } else if (orientation >= 225 && orientation < 315) {
                   rotation = Surface.ROTATION_90;
               } else {
                   rotation = Surface.ROTATION_0;
               }
               Log.i("CameraXTest", String.valueOf(rotation));
               Log.i("CameraXTest", String.valueOf(orientation));
               imageCapture.setTargetRotation(rotation);
           }
       };
       orientationEventListener.enable();
   }
  • 裁剪矩形
//剪裁矩形(拍摄之后,对图片进行裁剪)
              ViewPort viewPort = null;
              if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
                  viewPort = new ViewPort.Builder(
                          new Rational(100, 100), getDisplay().getRotation()
                  ).build();
              } else {
                  viewPort = new ViewPort.Builder(
                          new Rational(100, 100), Surface.ROTATION_0
                  ).build();
              }
              UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
                      .addUseCase(preview)
                      .addUseCase(imageAnalysis)
                      .addUseCase(imageCapture)
                      .addUseCase(videoCapture)
                      .setViewPort(viewPort)
                      .build();
              processCameraProvider.unbindAll();
              processCameraProvider.bindToLifecycle(MainActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, useCaseGroup);
控制相机输入

到了这里,关于【Android】一步步实现手机拍照、录像及存储至相册(CameraX)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 使用Camera1实现相机预览、拍照、录像

    本文介绍如何从零开始,在 Android 中实现 Camera1 的接入,并在文末提供 Camera1Manager 工具类,可以用于快速接入 Camera1 。 Android Camera1 API 虽然已经被 Google 废弃,但有些场景下不得不使用。 并且 Camera1 返回的帧数据是 NV21 ,不像 Camera2 、 CameraX 那样,需要自己再转一层,才能得

    2024年02月08日
    浏览(52)
  • Android相册选择图片、相机拍照上传功能实现(上)

    先上效果图 下面就来说一下相册选择图片和相机拍照的实现 相册选择图片很简单,只需要通过 Intent 设置拉起就可以了 Intent 拉起相册 /** 打开相册 @param type 打开类型区分码(type是我用来区分回调的) / private void openGallery(int type) { Intent gallery = new Intent(Intent.ACTION_PICK); galler

    2024年04月16日
    浏览(54)
  • Android Camera2-预览、拍照、录像流程

    一、Camera2实现预览、拍照、录像三大基础功能的流程框架图 Camera2关键几个类: CameraManager 管理手机上的所有摄像头设备。管理手机上的所有摄像头设备,它的作用主要是获取摄像头列表和打开(openCamera)指定的摄像头。 它其实是一个系统服务,通过getSystemService(Context.CAM

    2024年02月16日
    浏览(54)
  • Android开发 拍照+读取相册+保存到本地

    注册除了MainActivity的其他两个界面Albums和Camera,添加provider,申请使用相机的权限,读写权限 file_path.xml代码 如果虚拟机可以运行,手机不能安装,gradle.properties里面添加 文件结构 总结 https://wwzb.lanzoue.com/imUKH0n1nq4d 密码:1eda 分享Demo可试试效果 参考来源:  Android studio调用手机

    2024年02月05日
    浏览(62)
  • Compose使用OpenGL+CameraX快速实现相机“拍视频实时滤镜“、”拍照+滤镜“

    一、前言 短视频热潮还没有褪去,写这篇文章主要是帮助大部分人,能快速上手实现类似效果,实际上是: CameraX拿相机数据,OpenGL给CameraX提供一个Surface,数据放到OpenGL渲染的线程上去做图像相关操作 OpenGL滤镜来自 aserbao_AndroidCamera 视频录制核心代码参考改造自 Google 的 g

    2023年04月17日
    浏览(47)
  • Android 13 骁龙相机点击拍照流程分析(二)——点击拍照到存入相册

            本篇是在Android 13 骁龙相机点击拍照流程分析(一)——点击拍照到更新到左下角缩略图文章的基础上进行延申的,前面的预览、点击拍照的过程参考第一篇:Android 13 骁龙相机点击拍照流程分析(一)——点击拍照到更新到左下角缩略图-CSDN博客         从第一篇的

    2024年02月06日
    浏览(51)
  • Android 设置头像(拍照获取、相册获取、裁剪照片)

         在Android原生态开发过程中,往往会设计到用户头像的设置问题,一般来讲设置头像需要用到拍照、获取照片、存储照片、裁剪照片、显示照片等问题,本文将一步一步的进行说明讲解。 首先需要强调几点我在开发过程中遇到的问题。 权限问题,在Android6.0以后,Andr

    2024年02月13日
    浏览(44)
  • 安卓玩机搞机技巧综合资源-----手机隐藏拍照录像 取证软件 寻找隐藏摄像头 【十六】

    接上篇 安卓玩机搞机技巧综合资源------如何提取手机分区 小米机型代码分享等等 【一】 安卓玩机搞机技巧综合资源------开机英文提示解决dm-verity corruption your device is corrupt. 设备内部报错 AB分区等等【二】 安卓玩机搞机技巧综合资源------EROFS分区格式 小米红米机型分区类型

    2024年02月09日
    浏览(48)
  • Android权限申请、相册选择图片、拍照、获取联系人、页面跳转

    目前第一版封装了:页面启动、页面启动回调、获取通讯录联系人、获取相册图片、拍照获取图片、单条权限申请、多条权限申请,且已适配到Android 13 导入 权限申请 权限申请支持单条权限和多条权限申请,支持强制拒绝后弹窗提示用户并引导区设置也开启权限 默认强制拒

    2024年02月16日
    浏览(49)
  • Android 拍照以及相册中选择(适配高版本)————上传头像并裁剪(一)

           在项目研发中,相信大家都遇到过给用户增加 头像照片 的需求。        随着 手机版本的不断更新 ,android 8、android 9、android 10、android 12、android 13、鸿蒙系统等等;遇到这个功能需求,大家肯定会想, “这还不好写? 之前就已经写过了。” 把老项目跑了一遍

    2024年02月01日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包