官方发布
- Android Developer Document
- Google Demo
- Getting Started with CameraX
- News: Whats new in camerax
- News: Androids camerax jetpack library is now in beta
- Android Jetpack CameraX 库 Beta 版正式发布!
本文
从官方示例 Copy 而来,主要代码来自 Google Demo 的 CameraFragment
开始使用 CameraX
1. 导入依赖
在 app 或 module 的 build.gradle 中添加依赖:
dependencies {
def camerax_version = '1.0.0-beta02'
implementation "androidx.camera:camera-core:$camerax_version"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation 'androidx.camera:camera-view:1.0.0-alpha09'
}
配置 Java 版本 8 以上
android {
...
defaultConfig {
// Jetpack 库向前兼容的最低版本 Android L
minSdkVersion 21
...
}
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
2. 添加相机权限
<uses-permission android:name="android.permission.CAMERA" />
3. 添加 PreviewView
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
4. 请求相机权限(略)
5. 初始化相机,开启预览
- 选择摄像头(前置 or 后置)
private int lensFacing = CameraSelector.LENS_FACING_BACK;
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build();
- 初始化 UseCase (包括 PreView、ImageCapture、ImageAnalysis、VideoCapture)
private static final double RATIO_16_9 = 16.0 / 9.0;
private static final double RATIO_4_3 = 4.0 / 3.0;
private PreviewView previewView;
private Preview preview;
private ImageCapture imageCapture;
private void initUseCase() {
// PreView 的比例和方向
int ratio = getPreviewRatio();
int rotation = previewView.getDisplay().getRotation();
// 初始化相机预览
preview = new Preview.Builder()
.setTargetAspectRatio(ratio)
.setTargetRotation(rotation)
.build();
// 初始化捕获照片
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetAspectRatio(ratio)
.setTargetRotation(rotation)
.build();
}
// 确定比例
private int getPreviewRatio() {
DisplayMetrics displayMetrics = new DisplayMetrics();
previewView.getDisplay().getRealMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
double previewRatio = ((double) Math.max(width, height)) / Math.min(width, height);
if (Math.abs(previewRatio - RATIO_4_3) <= Math.abs(previewRatio - RATIO_16_9)) {
return AspectRatio.RATIO_4_3;
}
return AspectRatio.RATIO_16_9;
}
- 绑定相机
previewView = findViewById(R.id.preview_view);
previewView.post(this::bindCamera);
private void bindCamera() {
// 选择摄像头
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build();
// 获取 ProcessCameraProvider
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
// 初始化 UseCase
initUseCase();
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
cameraProvider.unbindAll();
// 绑定 UseCase 到相机
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
// 开始预览
preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo()));
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, ContextCompat.getMainExecutor(this));
}
6. 触发捕获照片
takePhotoView.setOnClickListener(v -> {
// 存储文件
File imgFile = Utils.createPhotoFile(outputFile);
// 存储原始数据,设置前置摄像头镜面配置
ImageCapture.Metadata metadata = new ImageCapture.Metadata();
metadata.setReversedHorizontal(lensFacing == CameraSelector.LENS_FACING_FRONT);
// 配置输出
ImageCapture.OutputFileOptions outputFileOptions =
new ImageCapture.OutputFileOptions.Builder(imgFile)
.setMetadata(metadata)
.build();
// 触发拍照
if (imageCapture != null) {
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
// 此处处理所得照片
Uri uri = outputFileResults.getSavedUri();
resultImg.setVisibility(View.VISIBLE);
Glide.with(resultImg)
.load(uri)
.into(resultImg);
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
exception.printStackTrace();
}
});
}
});
至此便可以实现简单的相机使用,包括预览和拍照。
完整代码:github.com/hanjx-dut/C…
- MainActivity
public class MainActivity extends AppCompatActivity {
private static final double RATIO_16_9 = 16.0 / 9.0;
private static final double RATIO_4_3 = 4.0 / 3.0;
private PreviewView previewView;
private View takePhotoView;
private ImageView resultImg;
private Preview preview;
private Camera camera;
private ImageCapture imageCapture;
private int lensFacing = CameraSelector.LENS_FACING_BACK;
private File outputFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
outputFile = Utils.getOutputFile(this);
previewView = findViewById(R.id.preview_view);
takePhotoView = findViewById(R.id.take_photo_img);
resultImg = findViewById(R.id.result_img);
previewView.post(this::bindCamera);
initListener();
}
private void initListener() {
takePhotoView.setOnClickListener(v -> {
File imgFile = Utils.createPhotoFile(outputFile);
ImageCapture.Metadata metadata = new ImageCapture.Metadata();
metadata.setReversedHorizontal(lensFacing == CameraSelector.LENS_FACING_FRONT);
ImageCapture.OutputFileOptions outputFileOptions =
new ImageCapture.OutputFileOptions.Builder(imgFile)
.setMetadata(metadata)
.build();
if (imageCapture != null) {
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Uri uri = outputFileResults.getSavedUri();
resultImg.setVisibility(View.VISIBLE);
Glide.with(resultImg)
.load(uri)
.into(resultImg);
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
exception.printStackTrace();
}
});
}
});
resultImg.setOnClickListener(v -> resultImg.setVisibility(View.GONE));
}
private void bindCamera() {
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing).build();
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
initUseCase();
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
cameraProvider.unbindAll();
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo()));
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, ContextCompat.getMainExecutor(this));
}
private void initUseCase() {
int ratio = getPreviewRatio();
int rotation = previewView.getDisplay().getRotation();
preview = new Preview.Builder()
.setTargetAspectRatio(ratio)
.setTargetRotation(rotation)
.build();
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetAspectRatio(ratio)
.setTargetRotation(rotation)
.build();
}
private int getPreviewRatio() {
DisplayMetrics displayMetrics = new DisplayMetrics();
previewView.getDisplay().getRealMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
double previewRatio = ((double) Math.max(width, height)) / Math.min(width, height);
if (Math.abs(previewRatio - RATIO_4_3) <= Math.abs(previewRatio - RATIO_16_9)) {
return AspectRatio.RATIO_4_3;
}
return AspectRatio.RATIO_16_9;
}
}
- Utils
class Utils {
static File getOutputFile(Context context) {
Context appContext = context.getApplicationContext();
File[] mediaDirs = context.getExternalMediaDirs();
if (mediaDirs != null && mediaDirs.length > 0 && mediaDirs[0] != null) {
File appMediaDir = new File(mediaDirs[0], appContext.getResources().getString(R.string.app_name));
appMediaDir.mkdirs();
return appMediaDir;
}
return appContext.getFilesDir();
}
static File createPhotoFile(File outputFile) {
return new File(outputFile, String.format("%s%s", System.currentTimeMillis(), ".png"));
}
}
- activity_main.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"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<View
android:id="@+id/action_area"
android:layout_width="match_parent"
android:layout_height="80dp"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#50000000" />
<ImageView
android:id="@+id/take_photo_img"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/take_photo"
app:layout_constraintTop_toTopOf="@id/action_area"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/result_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
- app/build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.cameraxdemo"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
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 fileTree(dir: 'libs', include: ['*.jar'])
// CameraX core library
def camerax_version = '1.0.0-beta02'
implementation "androidx.camera:camera-core:$camerax_version"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation 'androidx.camera:camera-view:1.0.0-alpha09'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}