目录
1. 基础开发环境
2. 添加相关依赖
3. APP 布局
4. 主流程逻辑
5. 调试或安装 APK
6. 项目完整代码
JDK:JDK17
Android Studio:Android Studio Giraffe | 2022.3.1
Android SDK:Android API 34
Gradle: gradle-8.0-bin.zip
CameraX Version: 1.1.0-alpha05
在 build.gradle 中添加 CameraX 的相关依赖
// *** Camera 相关依赖 ***
def cameraxVersion = "1.1.0-alpha05";
implementation "androidx.camera:camera-core:${cameraxVersion}"
implementation "androidx.camera:camera-camera2:${cameraxVersion}"
implementation "androidx.camera:camera-lifecycle:${cameraxVersion}"
implementation 'androidx.camera:camera-view:1.0.0-alpha25'
// ***********************
在 AndroidManifest.xml 文件中注册相机权限
使用 LinearLayout 布局,其中添加一个 PreviewView 用来显示相机画面的预览,添加一个 Button 用来控制录像开始和结束。
camera-video-capture
录像
package com.example.capture;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
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 android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutionException;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ListenableFuture processCameraProviderListenableFuture;
PreviewView previewView;
Button captureButton;
private VideoCapture videoCapture;
@Override
protected void onCreate(Bundle savedInstanceState) {
// onCreate 在活动被创建时被调用的。
// 它的作用是对活动进行初始化,例如加载布局文件,设置事件监听器和初始化变量等。
// `Bundle savedInstanceState` 参数用于保存活动状态,以便在活动被销毁后能够恢复它的状态。
super.onCreate(savedInstanceState);
// 将指定的布局文件加载到当前 Activity 中并显示在屏幕上。
setContentView(R.layout.activity_main);
// 从当前布局中查找具有指定 ID 的视图,并将其返回为 Java 对象。
previewView = findViewById(R.id.previewView);
captureButton = findViewById(R.id.captureButton);
// 将当前类实现的 OnClickListener 接口设置为 captureButton 的点击事件监听器,
// 以便在单击 captureButton 时调用类中的 onClick() 方法来处理点击事件。
captureButton.setOnClickListener(this);
// 这行代码的作用是获取相机提供者的实例,它是使用 Android CameraX API 实现相机功能的关键对象之一,
// 通过它可以获取相机设备、预览用例、图像分析用例等等,从而实现相机应用的各种功能。
// 此代码返回一个ListenableFuture对象,用于异步获取相机提供者的实例。
processCameraProviderListenableFuture = ProcessCameraProvider.getInstance(this);
// 监听摄像头的准备情况。准备好时,该代码块中的 start() 方法将被调用,以便启动相机。
processCameraProviderListenableFuture.addListener(() -> {
try {
ProcessCameraProvider processCameraProvider = processCameraProviderListenableFuture.get();
start(processCameraProvider);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
// 将监听器绑定到主线程,以确保在 UI 上下文中运行该代码块。
}, ContextCompat.getMainExecutor(this));
}
@SuppressLint("RestrictedApi")
private void start(ProcessCameraProvider processCameraProvider) {
// 取消当前已经绑定的摄像头设备,释放它们的资源,以便其他应用或者进程可以使用这些摄像头设备。
// 这个方法通常在摄像头应用程序退出或者暂停时调用,以确保摄像头设备不会一直占用系统资源。
processCameraProvider.unbindAll();
// 创建一个相机选择器对象,并指定选择前置摄像头。
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
.build();
// 创建一个相机预览对象
// 并将其与一个 SurfaceView 组件(previewView)的 SurfaceProvider 绑定,从而在该组件上显示相机预览画面。
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 创建一个视频捕捉对象
// 设置视频帧率为 30 帧/秒。这可以确保捕捉到的视频画面流畅,避免出现卡顿和不连贯的情况。
videoCapture = new VideoCapture.Builder()
.setVideoFrameRate(30)
.build();
// 在 Android 设备上启动相机,并将其与当前生命周期绑定,以便在应用程序暂停或停止时释放相机资源。
// 该方法接受一个 `CameraSelector` 对象用于选择相机设备,一个 `Preview` 对象用于显示预览,以及一个 `videoCapture` 对象用于捕获视频。
processCameraProvider.bindToLifecycle(this, cameraSelector, preview, videoCapture);
}
@SuppressLint("RestrictedApi")
@Override
public void onClick(View view) {
// onClick(View view) 的作用是为按钮或其他视图设置点击事件处理程序。
// 当用户点击该视图时,该方法会被调用并执行其中的代码。
if (view.getId() == R.id.captureButton) {
if (captureButton.getText() == "录像") {
captureButton.setText("停止");
captureVideo();
} else {
captureButton.setText("录像");
videoCapture.stopRecording();
}
}
}
@SuppressLint("RestrictedApi")
private void captureVideo() {
if (videoCapture != null) {
long timeStamp = System.currentTimeMillis();
// 通过 ContentValues 对象设置文件名和文件类型
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, timeStamp);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
if (ActivityCompat.checkSelfPermission(this, android.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(
// 第一个参数 OutputFileOptions 用于指定视频文件的输出位置和其他选项。
new VideoCapture.OutputFileOptions.Builder(
getContentResolver(),
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
contentValues
).build(),
// 第二个参数 Executor 指定用于指定视频录制的线程。
ContextCompat.getMainExecutor(this),
// 第三个参数 OnImageSavedCallback 用于在视频录制完成或出错时进行回调。
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {
Toast.makeText(MainActivity.this, "Saving...", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Toast.makeText(MainActivity.this, "Error: " + message, Toast.LENGTH_SHORT).show();
}
}
);
}
}
}
使用 USB 调试或者 Build 出 APK(Build -> Make Project)然后找到 app-debug.apk 文件进行安装。
注意:由于代码逻辑中没有权限申请部分,需要在安装好后手动开启拍照权限。
已经编译好的 APK 文件见文章开头,可直接下载安装。
https://gitee.com/hl0929/camera-video-capture