在今年的Google I/O大会上,Google新推出了CameraX支持包,按照官方的说法, 这个包的作用是:
help you make camera app development easier
安卓中使用相机从来就不是一件容易的事。
Camera1要自己管理Camera相机实例,要处理SufraceView相关的一堆东西,还有预览尺寸跟画面尺寸的选择,页面生命周期切换等等问题,后来推出了Camera2,从官方demo 就上千行代码来看,Camera2并不解决用起来复杂的问题,它提供了更多的调用接口,可定制性更好,结果就是对普通开发者来说更难用了。。。
终于Google也意识到这个问题,推出了最终版CameraX. CameraX实际上还是用的Camera2,但它对调用API进行了很好的封装,使用起来非常方便。官方教程也很详细,如下:
https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0
注意:CameraX跟Camera2一样最低支持API21,也就是5.0及以上。
开发环境用Android Studio3.3及以上,依赖库都用androidx的
创建app
创建app,声明相机权限
<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.RECORD_AUDIO" />
添加相关依赖
// CameraX core library def camerax_version = "1.0.0-alpha05" // CameraX view library def camerax_view_version = "1.0.0-alpha02" // CameraX extensions library def camerax_ext_version = "1.0.0-alpha02" implementation "androidx.camera:camera-core:$camerax_version" // If you want to use Camera2 extensions implementation "androidx.camera:camera-camera2:$camerax_version" // If you to use the Camera View class implementation "androidx.camera:camera-view:$camerax_view_version" // If you to use Camera Extensions implementation "androidx.camera:camera-extensions:$camerax_ext_version"
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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.CameraView
android:id="@+id/view_camera"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/btn_take_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
android:text="拍照" />
<Button
android:id="@+id/btn_start_recording"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
android:layout_toRightOf="@+id/btn_take_pic"
android:text="@string/start_recording" />
<Button
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
android:layout_toRightOf="@+id/btn_start_recording"
android:text="切换摄像头" />
<ImageView
android:id="@+id/iv_show_pic"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp" />
<VideoView
android:id="@+id/vv_show_recording"
android:layout_width="100dp"
android:layout_height="150dp"
android:visibility="gone" />
</RelativeLayout>
切记在启动该Activity的时候要动态获取到相机权限 读写权限,麦克风权限
我demo 中引入的是:
implementation 'com.blankj:utilcodex:1.26.0' 动态获取取权限:
public void getPermission() {
PermissionUtils.permission(PermissionConstants.STORAGE, PermissionConstants.CAMERA, PermissionConstants.MICROPHONE)
.rationale(new PermissionUtils.OnRationaleListener() {
@Override
public void rationale(final ShouldRequest shouldRequest) {
shouldRequest.again(true);
}
})
.callback(new PermissionUtils.FullCallback() {
@Override
public void onGranted(List<String> permissionsGranted) {
startActivity(new Intent(WelcomeActivity.this, MainActivity.class));
}
@Override
public void onDenied(List<String> permissionsDeniedForever,
List<String> permissionsDenied) {
if (!permissionsDeniedForever.isEmpty()) {
PermissionUtils.launchAppDetailsSettings();
}
}
}).request();
}
onGranted回调是获取到了所有权限,获取到所有权限后就可以进入到相机界面,这时候进入相机界面相机后置摄像头就默认打开了,也就是进入了Preview状态
相机界面首先: CameraView 的 bindToLifeCycle 方法将这个 View 与当前组件的生命周期绑定:
mCameraView.bindToLifecycle(this);
拍照:
if (mCameraView == null) {
return;
}
mCameraView.setCaptureMode(CameraView.CaptureMode.IMAGE);
mCameraView.takePicture(initTakePicPath(), new ImageCapture.OnImageSavedListener() {
@Override
public void onImageSaved(@NonNull File file) {
LogUtils.d("MainActivity takePicture onImageSaved file : " + file);
if (mShowPicView == null) {
return;
}
try {
Glide.with(MainActivity.this)
.load(file)
.into(mShowPicView);
} catch (Exception e) {
}
}
@Override
public void onError(@NonNull ImageCapture.ImageCaptureError imageCaptureError, @NonNull String message,
@Nullable Throwable cause) {
LogUtils.d("MainActivity takePicture onError imageCaptureError : " + imageCaptureError + " message : " + message +
" Throwable : " + cause);
}
});
录像:
if (mCameraView == null) {
return;
}
if (mCameraView.isRecording()) {
mCameraView.stopRecording();
mStartRecordingView.setText(ResourceUtil.getString(R.string.start_recording));
} else {
mStartRecordingView.setText(ResourceUtil.getString(R.string.stop_recording));
mCameraView.setCaptureMode(CameraView.CaptureMode.VIDEO);
mCameraView.startRecording(initStartRecordingPath(), new VideoCapture.OnVideoSavedListener() {
@Override
public void onVideoSaved(@NonNull File file) {
LogUtils.d("MainActivity startRecording onVideoSaved file : " + file);
if (mShowVideoView == null) {
return;
}
mShowVideoView.post(new Runnable() {
@Override
public void run() {
mShowVideoView.setVisibility(View.VISIBLE);
mShowVideoView.setVideoPath(file.getAbsolutePath());
mShowVideoView.start();
mShowVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mShowVideoView.start();
}
});
}
});
}
@Override
public void onError(@NonNull VideoCapture.VideoCaptureError videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
LogUtils.d("MainActivity startRecording onError imageCaptureError : " + videoCaptureError + " message : " + message +
" Throwable : " + cause);
}
});
}
注意:1:录像回来是子线程 如果想使用VideoView播放该视频 需要切换到UI线程 2.录像钱要切换:CaptureMode 否则会报错:
throw new IllegalStateException("Can not record video under IMAGE capture mode.");
看下源码就知道:
public void startRecording(File file, final OnVideoSavedListener listener) {
if (mVideoCapture == null) {
return;
}
if (getCaptureMode() == CaptureMode.IMAGE) {
throw new IllegalStateException("Can not record video under IMAGE capture mode.");
}
if (listener == null) {
throw new IllegalArgumentException("OnVideoSavedListener should not be empty");
}
mVideoIsRecording.set(true);
mVideoCapture.startRecording(
file,
new VideoCapture.OnVideoSavedListener() {
@Override
public void onVideoSaved(@NonNull File savedFile) {
mVideoIsRecording.set(false);
listener.onVideoSaved(savedFile);
}
@Override
public void onError(
@NonNull VideoCapture.VideoCaptureError videoCaptureError,
@NonNull String message,
@Nullable Throwable cause) {
mVideoIsRecording.set(false);
Log.e(TAG, message, cause);
listener.onError(videoCaptureError, message, cause);
}
});
}
切换摄像头:
if (mCameraView == null) {
return;
}
CameraX.LensFacing lensFacing = mCameraView.getCameraLensFacing();
if (lensFacing == null) {
return;
}
if (lensFacing == CameraX.LensFacing.FRONT) {
mCameraView.setCameraLensFacing(CameraX.LensFacing.BACK);
} else {
mCameraView.setCameraLensFacing(CameraX.LensFacing.FRONT);
}
demo已经上传到Github 上了:https://github.com/HuaDanJson/CameraX
CameraX是Google为简化Android相机应用开发推出的支持包,封装了Camera2的复杂性,提供更简洁的API。本文详细介绍如何使用CameraX进行相机应用开发,包括权限设置、依赖添加、布局配置及拍照、录像、切换摄像头等功能实现。
1547

被折叠的 条评论
为什么被折叠?



