CameraX使用

前言

官方介绍 CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。如果您要开发新应用,我们建议您从 CameraX 开始。它提供了一个一致且易于使用的 API,该 API 适用于绝大多数 Android 设备,并向后兼容 Android 5.0(API 级别 21)。
这章主要讲述 预览,拍照,录像,切换摄像头

1 环境
android studio4.1.1
cameraX1.2.0

2 工程说明
1> 布局
activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照录像"
        android:onClick="recordimages"
        />

</LinearLayout>

activity_preview.xml

<?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">

    <Button
        android:id="@+id/btnCapture"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginBottom="50dp"
        android:elevation="2dp"
        android:scaleType="fitCenter"
        android:text="Take Photo"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/btnVideo"
        android:onClick ="takePhoto"
        />

    <Button
        android:id="@+id/btnVideo"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:elevation="2dp"
        android:scaleType="fitCenter"
        android:text="Start Video"
        app:layout_constraintBottom_toBottomOf="@+id/btnCapture"
        app:layout_constraintEnd_toStartOf="@id/btnCapture"
        app:layout_constraintStart_toEndOf="@+id/btnSwitch"
        android:onClick="videoRecord"/>


    <Button
        android:id="@+id/btnSwitch"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:elevation="2dp"
        android:scaleType="fitCenter"
        android:text="Switch Camera"
        app:layout_constraintBottom_toBottomOf="@+id/btnCapture"
        app:layout_constraintEnd_toStartOf="@id/btnVideo"
        app:layout_constraintStart_toStartOf="parent"
        android:onClick="cameraSwitch"/>
    <androidx.camera.view.PreviewView
        android:id="@+id/viewFinder"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testcamerax">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!--录制音频权限-->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Testcamerax">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".PreviewActivity"/>
    </application>

</manifest>

app 下 build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 33
    buildToolsVersion "33.0.0"

    defaultConfig {
        applicationId "com.example.testcamerax"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'


    def camerax_version = "1.2.0"    //"1.1.0-beta03"
// CameraX core library
    implementation "androidx.camera:camera-core:$camerax_version"
// CameraX Camera2 extensions[可选]拓展库可实现人像、HDR、夜间和美颜、滤镜但依赖于OEM
    implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle library[可选]避免手动在生命周期释放和销毁数据
    implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class[可选]最佳实践,最好用里面的PreviewView,它会自行判断用SurfaceView还是TextureView来实现
    implementation "androidx.camera:camera-view:$camerax_version"

    implementation "androidx.camera:camera-extensions:${camerax_version}"

    // If you want to additionally use the CameraX VideoCapture library
    implementation "androidx.camera:camera-video:${camerax_version}"


    // CameraX core library using the camera2 implementation
  //  def camerax_version = "1.2.0-alpha02" //1.2.0-alpha02
// The following line is optional, as the core library is included indirectly by camera-camera2
 /*   implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to additionally use the CameraX Lifecycle library
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
// If you want to additionally use the CameraX VideoCapture library
    implementation "androidx.camera:camera-video:${camerax_version}"
// If you want to additionally use the CameraX View class
    implementation "androidx.camera:camera-view:${camerax_version}"
// If you want to additionally add CameraX ML Kit Vision Integration
    implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
// If you want to additionally use the CameraX Extensions library
    implementation "androidx.camera:camera-extensions:${camerax_version}"*/
}

2 > 代码
MainActivity.java

package com.example.testcamerax;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.View;
import android.widget.Toast;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {
    private final String TAG = MainActivity.class.getName() ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }


    //启动拍照,录像的页
    public void recordimages(View v){

        startActivity(new Intent(this, PreviewActivity.class));
      //  mCameraPRrovider.
    }

//        button2.setOnClickListener(new OnClickListener(){
//
//        @Override
//        public void onClick(View v) {
//            // TODO Auto-generated method stub
//            Toast.makeText(MainActivity.this, "haha 你点了button2",
//                    Toast.LENGTH_LONG).show();
//        }
//
//    });

}

PreviewActivity.java

package com.example.testcamerax;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;

import androidx.camera.core.VideoCapture;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;

import com.google.common.util.concurrent.ListenableFuture;

import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static androidx.camera.core.ImageCapture.*;

public class PreviewActivity extends AppCompatActivity {
    public  static  String TAG = PreviewActivity.class.getName() ;
    private ExecutorService cameraExecutor;
    private ProcessCameraProvider mCameraPRrovider = null;
    public static final int DMS_INPUT_IMG_W = 640;
    public static final int DMS_INPUT_IMG_H = 480;
    @RequiresApi(21)
    private ImageCapture imageCapture;
    @SuppressLint("RestrictedApi")
    private VideoCapture videoCapture;
    private CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;//后置相机
    private final int REQUEST_CODE_CONTACT = 1;
    private Boolean mRecording = false;
    private Button    bVideoRecord  ;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preview);
        bVideoRecord = (Button)findViewById(R.id.btnVideo);
        requestAllPermission();
    }

    private ImageAnalysis imageAnalysis(ExecutorService cameraExecutor) {
        //
        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                .setTargetResolution(new Size(DMS_INPUT_IMG_W, DMS_INPUT_IMG_H)) // 图片的建议尺寸
                .setOutputImageRotationEnabled(true) // 是否旋转分析器中得到的图片
                .setTargetRotation(Surface.ROTATION_0) // 允许旋转后 得到图片的旋转设置
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .setImageQueueDepth(1)
                .build();
        //  imageAnalysis.setAnalyzer(cameraExecutor, new MyAnalyzer());
        imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
            int picformat = imageProxy.getFormat();
            if (imageProxy.getFormat() == ImageFormat.YUV_420_888) {

            } else if (imageProxy.getFormat() == ImageFormat.FLEX_RGBA_8888 || picformat == PixelFormat.RGBA_8888) {

            }

            imageProxy.close(); // 最后要关闭这个
        });
        return imageAnalysis;
        
    }

    private Preview previewCreate() {
        //
        //要预览开启下面3句 + processCameraProvider.bindToLifecycle(MainActivity.this, cameraSelector,
        //                        imageAnalysis,preview);  preview 加上,不要预览就去跳
        // 创建一个Preview 实例,并设置该实例的 surface 提供者(provider)。
        PreviewView viewFinder = (PreviewView) findViewById(R.id.viewFinder);
        Preview preview = new Preview.Builder()
                .build();
        preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
        return preview;
        ///
    }

    //绑定
    private Camera carmerabindToLifecycle(ProcessCameraProvider cameraProvider, LifecycleOwner lifecycleOwner, CameraSelector cameraSelector, Preview preview, ImageAnalysis imageAnalysis, ImageCapture imageCapture) {

        return cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis, imageCapture);
    }

    private ImageCapture imageCaptureCreate(PreviewView previewView) {
        //设置宽高比
        //    ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9)
//设置实际的尺寸
        //    ImageAnalysis.Builder().setTargetResolution(Size(1920, 1080))
        int screenAspectRatio = AspectRatio.RATIO_16_9; //Tools.aspectRatio(this);
        int rotation = Surface.ROTATION_0;// previewView.getDisplay() == null ? Surface.ROTATION_0 : previewView.getDisplay().getRotation();

        ImageCapture imageCapture = new Builder()
                //优化捕获速度,可能降低图片质量
                .setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)
                .setTargetAspectRatio(screenAspectRatio)
                .setTargetRotation(rotation)
                .build();

        return imageCapture;
    }

    private VideoCapture videoRecordCreate() {
        int screenAspectRatio = AspectRatio.RATIO_16_9; //Tools.aspectRatio(this);
        int rotation = Surface.ROTATION_0;
        @SuppressLint("RestrictedApi") VideoCapture VideoCapture = new VideoCapture.Builder()
//                .setTargetAspectRatio(screenAspectRatio) // 设置宽高比例
//                .setVideoFrameRate(60) // 设置视频帧率
//                .setBitRate(3 * 1024 * 1024) // 设置比特率
//                .setTargetRotation(rotation) // 设置旋转角度
//                .setAudioRecordSource(MediaRecorder.AudioSource.MIC)
                .build();
        return VideoCapture;
        //  if (mCameraMode == MODE_RECORD) { // 录像
        // 构建一个视频捕捉器

        // }
    }

    private void bindCaptureUsecase() {
//        val cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
//        // 选择前置/后置摄像头
//        val cameraSelector = getCameraSelector(cameraIndex)
//        // 选择分辨率
//        val quality = cameraCapabilities[cameraIndex].qualities[qualityIndex]
//        val qualitySelector = QualitySelector.from(quality)
//        // 连接preview到previewView
//        val preview = Preview.Builder()
//                .setTargetAspectRatio(quality.getAspectRatio(quality))
//                .build()
//        preview.setSurfaceProvider(captureViewBinding.previewView.surfaceProvider)
        // 创建videoCapture对象
//        val recorder = Recorder.Builder()
//                .setQualitySelector(qualitySelector)
//                .build()
//        videoCapture = VideoCapture.withOutput(recorder)
//        try {
//            // 绑定生命周期
//            cameraProvider.unbindAll()
//            cameraProvider.bindToLifecycle(viewLifecycleOwner, cameraSelector, videoCapture, preview)
//        } catch (e: Exception) {
//            Log.e(TAG, "VideoCapture binding failed:$e")
//        }
    }

    //
    //init camera
    private void initCamera() {
        // 将Camera的生命周期和Activity绑定在一起(设定生命周期所有者),这样就不用手动控制相机的启动和关闭。
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraExecutor = Executors.newSingleThreadExecutor();

        cameraProviderFuture.addListener(() -> {
            // 将你的相机和当前生命周期的所有者绑定所需的对象
            try {
                mCameraPRrovider = cameraProviderFuture.get();

                //
                //要预览开启下面3句 + processCameraProvider.bindToLifecycle(MainActivity.this, cameraSelector,
                //                        imageAnalysis,preview);  preview 加上,不要预览就去跳
                // 创建一个Preview 实例,并设置该实例的 surface 提供者(provider)。
                PreviewView viewFinder = (PreviewView) findViewById(R.id.viewFinder);
                Preview preview = new Preview.Builder()
                        .build();
                preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
                ///
                //选择摄像头
                // 选择前置摄像头作为默认摄像头
              //  CameraSelector cameraSelector = isBackCamera?CameraSelector.DEFAULT_BACK_CAMERA:CameraSelector.DEFAULT_FRONT_CAMERA;
                //选择其他ID 摄像头
     //           int  mCameraId =1 ; //1 前置  0 后置
     //           CameraSelector cameraSelector = new CameraSelector.Builder().addCameraFilter(new MyCameraFilter(mCameraId)).build() ;
                /
                imageCapture = new ImageCapture.Builder().build();//拍照用例配置
                //
                // 设置预览帧分析
//                ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
//                        .build();
                ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                        .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                        .setTargetResolution(new Size(DMS_INPUT_IMG_W, DMS_INPUT_IMG_H)) // 图片的建议尺寸
                        .setOutputImageRotationEnabled(true) // 是否旋转分析器中得到的图片
                        .setTargetRotation(Surface.ROTATION_0) // 允许旋转后 得到图片的旋转设置
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .setImageQueueDepth(1)
                        .build();
                imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
                    final int picformat = imageProxy.getFormat();
                    if (picformat == ImageFormat.YUV_420_888) {

                    } else if (picformat == ImageFormat.FLEX_RGBA_8888 || picformat == PixelFormat.RGBA_8888) {

                    }
                    imageProxy.close();
                });
                //
                //录像用例配置
                videoCapture = new VideoCapture.Builder()
//                .setTargetAspectRatio(screenAspectRatio) // 设置宽高比例
//                .setVideoFrameRate(60) // 设置视频帧率
//                .setBitRate(3 * 1024 * 1024) // 设置比特率
//                .setTargetRotation(rotation) // 设置旋转角度
//                .setAudioRecordSource(MediaRecorder.AudioSource.MIC)
                        .build();
                //
                mCameraPRrovider.unbindAll();//先解绑所有用例
                //因为camera 同时只能绑定3种use case,所以本节在拍照、摄像、预览、分析中,选择了前3种用途,代码如下:
                mCameraPRrovider.bindToLifecycle(PreviewActivity.this, cameraSelector, preview, imageCapture,videoCapture); //imageAnalysis,

            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            预览配置

        }, ContextCompat.getMainExecutor(this));
    }

    private void requestAllPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.INTERNET,
                    Manifest.permission.CAMERA,
                    Manifest.permission.RECORD_AUDIO,
            }; //Manifest.permission.RECORD_AUDIO,
            for (String str : permissions) {
                if (ContextCompat.checkSelfPermission(this, str) != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_CONTACT);
                    return;
                }
            }

            initCamera();

        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_CONTACT) {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // doInitWork();
                initCamera();
            } else if (grantResults.length == 0) {
                // 已知魅族手机会进这个结果
            } else {
                Toast.makeText(this, "您拒绝了相关权限,无法使用!", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    //拍照
    public void takePhoto(View v) {
//        File dir = new File(Constants.PATH);
//        if (!dir.exists()) {
//            dir.mkdirs();
//        }
//        //创建文件
//        File file = new File(Constants.PATH,"testx.jpg");
//        if (file.exists()) {
//            file.delete();
//        }
        String photofilepath = getFilesPath(this);
        ///storage/emulated/0/Android/data/com.example.testcamerax/files
        File file = new File(photofilepath + File.separator + "20230222.jpg");
        if (file.exists()) {
            file.delete();
        }

        ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(file).build();//new File(photoFile)

        //  设置图像捕获监听器,在拍照后触发
        imageCapture.takePicture(
                outputOptions,
                ContextCompat.getMainExecutor(this),
                new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {

                        Toast.makeText(PreviewActivity.this, "保存成功: ", Toast.LENGTH_SHORT).show();
//                        rl_start.setVisibility(View.GONE);
//                        rl_result_picture.setVisibility(View.VISIBLE);
//                        ll_picture_parent.setVisibility(View.VISIBLE);
//                        Bitmap bitmap = Tools.bitmapClip(CameraActivity.this, photoFile, front);
//                        img_picture.setImageBitmap(bitmap);
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Log.e("wld_____", "Photo capture failed: ${exc.message}", exception);
                    }
                });
    }

    @SuppressLint("RestrictedApi")
    private void startVideo() {

        String photofilepath = getFilesPath(this);
///storage/emulated/0/Android/data/com.example.testcamerax/files
        File file = new File(photofilepath + File.separator + "20230222.mp4");
        if (file.exists()) {
            file.delete();
        }

        //  ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
        
        VideoCapture.OutputFileOptions build = new VideoCapture.OutputFileOptions.Builder(file).build();

        // 开始录像动作
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        videoCapture.startRecording(build, Executors.newSingleThreadExecutor(), new VideoCapture.OnVideoSavedCallback() {
            @Override
            public void onVideoSaved(VideoCapture.OutputFileResults outputFileResults) {

                //file:///storage/emulated/0/Android/data/com.example.testcamerax/files/20230222.mp4
                Log.e(TAG, "onVideoSaved: " + outputFileResults.getSavedUri());
              //  Toast.makeText(PreviewActivity.this, "录制结束", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(int videoCaptureError, String message, Throwable cause) {
                Log.e(TAG, "onVideoSaved: fail"+message );
            }
        });
        

//        //开始录像
//        videoCapture.startRecording(outputOptions,new  Executors.newSingleThreadExecutor());
//    //    videoCapture.startRecording(file, Executors.newSingleThreadExecutor(), object :
//        VideoCapture.OnVideoSavedCallback {
//            override fun onVideoSaved(@NonNull file: File) {
//                //保存视频成功回调,会在停止录制时被调用
//                Log.i(TAG,"onVideoSaved: ${file.absoluteFile}")
//            }
//
//            override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
//                //保存失败的回调,可能在开始或结束录制时被调用
//                Log.i(TAG,"onError: $message")
//            }
//        })
    }

    @SuppressLint("RestrictedApi")
    private void stopVideo() {
      //  btnVideo.text = "Start Video"
        //结束录像
        videoCapture.stopRecording();//停止录制
    }

	//录像
    public   void videoRecord(View v){
        if(!mRecording){  //没开始
            bVideoRecord.setText("Stop Video");
            startVideo();
        }else{
            bVideoRecord.setText("Start Video");
            stopVideo();
        }
        mRecording = !mRecording ;
    }

    @SuppressLint("RestrictedApi")
    public void cameraSwitch(View v){
        if( cameraSelector.getLensFacing() == CameraSelector.DEFAULT_BACK_CAMERA.getLensFacing()) {//后置相机
            cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
        }else{
            cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
        }
        cameraExecutor.shutdown();
        initCamera();
    }


   // 函数返回路径/storage/emulated/0/Android/data/包名/files
   // 用来存储一些长时间保留的数据,应用卸载会被删除
    public String getFilesPath( Context context ){
        String filePath ;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            //外部存储可用
            filePath = context.getExternalFilesDir(null).getPath();
        }else {
            //外部存储不可用
            filePath = context.getFilesDir().getPath() ;
        }
        return filePath ;
    }

    //函数返回路径/storage/emulated/0/Android/data/包名/cache
    //用来存储一些临时缓存数据
    public String getCachePath( Context context ){
        String cachePath ;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            //外部存储可用
            cachePath = context.getExternalCacheDir().getPath() ;
        }else {
            //外部存储不可用
            cachePath = context.getCacheDir().getPath() ;
        }
        return cachePath ;
    }

    ///
//    private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
//
//        private fun ByteBuffer.toByteArray(): ByteArray {
//            rewind()
//            val data = ByteArray(remaining())
//            get(data)
//            return data
//        }
//
//        override fun analyze(image: ImageProxy) {
//            val buffer = image.planes[0].buffer
//            val data = buffer.toByteArray()
//            val pixels = data.map { it.toInt() and 0xFF }
//            val luma = pixels.average()
//
//            listener(luma)
//
//            image.close()
//        }
//    }
    /

}

3>代码说明
//选择其他ID 摄像头
// int mCameraId =1 ; //1 前置 0 后置
// CameraSelector cameraSelector = new CameraSelector.Builder().addCameraFilter(new MyCameraFilter(mCameraId)).build() ;
需要文件MyCameraFilter.java 放在 PreviewActivity.java 同目录下即可

import android.annotation.SuppressLint;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
import androidx.camera.core.CameraFilter;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.Identifier;
//import androidx.core.util.Preconditions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class MyCameraFilter implements CameraFilter {
    private  int carmeraid = 0;
    private  String strcarmeraid = "";
    public MyCameraFilter(int tcarmerid) {
        carmeraid = tcarmerid ;
        strcarmeraid = String.valueOf(tcarmerid);
    }
    @SuppressLint("RestrictedApi")
    @NonNull
    @Override
    public List<CameraInfo> filter(@NonNull List<CameraInfo> cameraInfos) {
        //return null;
        List<CameraInfo> output = new ArrayList<>();
        for(CameraInfo it : cameraInfos){
        //    Preconditions.checkArgument(it instanceof CameraInfoInternal,)
            //instanceof, isInstance,isAssignableFrom
            if(it instanceof Camera2CameraInfoImpl){
                Camera2CameraInfoImpl it2 = (Camera2CameraInfoImpl)it ;
                if(strcarmeraid.equalsIgnoreCase(it2.getCameraId())){
                    output.add(it);
                    break; //打开单个
                }
            }
        }
        return  output;
//        List<CameraInfo> output = new ArrayList<>(cameraInfos);
//        for (Camcarmerx  usb 摄像头  CameraInfoInternaleraFilter filter : mCameraFilterSet) {
//            output = filter.filter(Collections.unmodifiableList(output));
//        }
//
//        output.retainAll(cameraInfos);
//        return output;

        //List<CameraInfo> output = new ArrayList<>();
    }
}

3运行结果
在这里插入图片描述

4 后续如需可以上传DEMO工程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
非常好的问题,使用CameraX可以轻松地实现拍照功能。首先需要在项目的build.gradle文件中添加以下依赖项: ``` implementation "androidx.camera:camera-core:1.0.0" implementation "androidx.camera:camera-camera2:1.0.0" ``` 然后,在XML布局文件中添加一个ImageView控件用于显示照片,以及一个Button控件用于触发拍照: ``` <ImageView android:id="@+id/photo_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <Button android:id="@+id/camera_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Take Photo"/> ``` 接下来,在Activity或Fragment中,可以使用以下代码来初始化CameraX,并启动相机预览: ``` val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener(Runnable { val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() // 创建 Preview 对象 val preview = Preview.Builder() .build() // 创建 ImageCapture 对象 val imageCapture = ImageCapture.Builder() .build() // 绑定预览对象和 ImageCapture 对象到相机上 val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA cameraProvider.unbindAll() camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) // 预览的 Surface val previewSurfaceProvider = previewView.previewSurfaceProvider preview.setSurfaceProvider(previewSurfaceProvider) }, ContextCompat.getMainExecutor(this)) ``` 最后,在Button的Click事件中,可以使用以下代码来拍照: ``` val imageCapture = imageCapture ?: return@setOnClickListener val photoFile = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg") val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build() imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // 显示照片 val savedUri = Uri.fromFile(photoFile) photo_view.post { photo_view.setImageURI(savedUri) } } override fun onError(exception: ImageCaptureException) { // 错误处理 } }) ``` 希望这些代码可以帮助您实现使用CameraX进行拍照。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值