JetPack 개발 에 서 는 CameraX 를 사용 하여 사진 촬영 과 동 영상 촬영 기능 을 완성 합 니 다.
카메라 1 과 카메라 2 를 사용 해 카메라 기능 을 개발 할 때 는 매우 복잡 한 API 를 사용 해 야 했 고,안 드 로 이 드 폰 의 파편 화가 심해 카메라 기능 에 대한 지원 도가 달라 카메라 관련 애플 리 케 이 션 을 하 는 회사 들 이 카메라 사용 절 차 를 간소화 하고 호환성 문 제 를 처리 하 는 경우 가 많 았 다.
CameraX 는 사실 Google 이 개발 한 카메라 개발 시 API 호출 을 간소화 하고 다양한 호환성 문 제 를 처리 하 는 라 이브 러 리 입 니 다.최대 안 드 로 이 드 5.0 까지 호 환 되 며,바 텀 호출 도 Camera 2 이지 만,Camera 2 보다 더 간단 하 며,라 이 프 사이클 을 연결 해 카메라 의 켜 기 방출 등 을 자동 으로 처리 할 수 있다.
다음 부터 해 보도 록 하 겠 습 니 다.
의존 도 를 높이다
dependencies {
// CameraX camera2
implementation "androidx.camera:camera-camera2:1.0.0-beta03"
// CameraView
implementation "androidx.camera:camera-view:1.0.0-alpha10"
//
implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
//camerax
implementation "androidx.camera:camera-lifecycle:1.0.0-beta03"
}
CameraX 로 사진 을 찍 으 려 면 사용 상 태 를 다 르 게 설정 한 뒤 라 이 프 사이클 에 연결 하면 된다.예 를 들 어 미리 보 기 는 미리 보기 와 관련 된 상 태 를 설정 해 야 하고 사진 을 찍 으 려 면 사진 촬영 과 관련 된 상 태 를 설정 해 야 하 며 영상 을 녹화 하려 면 녹화 와 관련 된 상 태 를 설정 해 야 한다.설정 상태
미리 보기 설정:Preview 는 카메라 미리 보기 에 미리 보기 화면 을 표시 합 니 다.
Preview preview = new Preview.Builder()
//
.setTargetAspectRatio(screenAspectRatio)
//
.setTargetRotation(rotation)
.build();
사진 설정:ImageCapture 는 사진 을 찍 고 그림 을 저장 합 니 다.
ImageCapture imageCapture = new ImageCapture.Builder()
// ,
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
//
.setTargetAspectRatio(screenAspectRatio)
//
.setTargetRotation(rotation)
.build();
녹화 영상 설정:VideoCapture 는 영상 을 녹화 하고 저장 하 는 데 사용 되 며 너비 와 해상 도 를 하나 설정 하면 됩 니 다.동시에 설정 하지 마 십시오.그렇지 않 으 면 오 류 를 보고 합 니 다.실제 수요 에 따라 설정 하고 너비 와 높이 에 대한 요구 가 높 으 면 너비 와 높이 비 를 설정 하 며 반대로 해상 도 를 설정 합 니 다.
VideoCapture videoCapture = new VideoCaptureConfig.Builder()
//
.setTargetRotation(rotation)
//
.setTargetAspectRatio(screenAspectRatio)
//
//.setTargetResolution(resolution)
//
.setVideoFrameRate(25)
//bit
.setBitRate(3 * 1024 * 1024)
.build();
라 이 프 사이클 에 연결:ProcessCamera Provider 는 하나의 예 로 카메라 의 라 이 프 사이클 을 모든 LifecycleOwner 클래스 에 연결 할 수 있 습 니 다.AppCompat Activity 와 Fragment 는 모두 Lifecycle Owner 입 니 다.
//Future ,ListenableFuture ,
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(this);
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
//
cameraProvider.unbindAll();
Camera camera = cameraProvider.bindToLifecycle(CameraActivity.this,
cameraSelector,preview,imageCapture,videoCapture);
OK 미리 보기,사진 찍 기,동 영상 녹화 설정 및 라 이 프 사이클 연결 작업 완료미리 볼 때 View 컨트롤 에 표시 해 야 합 니 다.Camera X 에 서 는 미리 보기 화면 을 표시 하 는 데 사용 되 는 PreviewView 를 제공 합 니 다.그 내 부 는 TextureView 와 SurfaceView 를 봉 하여 서로 다른 패턴 에 따라 내 부 를 TextureView 로 표시 할 지 SurfaceView 로 표시 할 지 선택 할 수 있 습 니 다.
xml 에 PreviewView 를 추가 하고 코드 에 앞에서 만 든 Preview 인 스 턴 스 에 추가 합 니 다.
<androidx.camera.view.PreviewView
android:id="@+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera .getCameraInfo()));
이렇게 하면 우리 가 이 페이지 에 들 어 갈 때 카메라 의 미리보기 효 과 를 볼 수 있 습 니 다.다음은 사진 찍 기와 녹화 기능 을 수행 하 는 것 입 니 다.포 토 비디오 를 실행 하 다.
사진 찍 기:
//
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(),
System.currentTimeMillis() + ".jpeg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
mImageCapture.takePicture(outputFileOptions,mExecutorService , new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Uri savedUri = outputFileResults.getSavedUri();
if(savedUri == null){
savedUri = Uri.fromFile(file);
}
outputFilePath = file.getAbsolutePath();
onFileSaved(savedUri);
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Log.e(TAG, "Photo capture failed: "+exception.getMessage(), exception);
}
});
//
private void onFileSaved(Uri savedUri) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri));
}
String mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap
.getFileExtensionFromUrl(savedUri.getPath()));
MediaScannerConnection.scanFile(getApplicationContext(),
new String[]{new File(savedUri.getPath()).getAbsolutePath()},
new String[]{mimeTypeFromExtension}, new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
Log.d(TAG, "Image capture scanned into media store: $uri"+uri);
}
});
PreviewActivity.start(this, outputFilePath, !takingPicture);
}
//
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(),
System.currentTimeMillis() + ".mp4");
mVideoCapture.startRecording(file, Executors.newSingleThreadExecutor(), new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull File file) {
outputFilePath = file.getAbsolutePath();
onFileSaved(Uri.fromFile(file));
}
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Log.i(TAG,message);
}
});
videoCapture.stopRecording();
코드:
public class RecordView extends View implements View.OnLongClickListener, View.OnClickListener {
private static final int PROGRESS_INTERVAL = 100;
private int mBgColor;
private int mStrokeColor;
private int mStrokeWidth;
private int mDuration;
private int mWidth;
private int mHeight;
private int mRadius;
private int mProgressValue;
private boolean isRecording;
private RectF mArcRectF;
private Paint mBgPaint, mProgressPaint;
private OnRecordListener mOnRecordListener;
private long mStartRecordTime;
public void setOnRecordListener(OnRecordListener onRecordListener) {
mOnRecordListener = onRecordListener;
}
public RecordView(Context context) {
this(context, null);
}
public RecordView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RecordView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordView);
mBgColor = typedArray.getColor(R.styleable.RecordView_bg_color, Color.WHITE);
mStrokeColor = typedArray.getColor(R.styleable.RecordView_stroke_color, Color.RED);
mStrokeWidth = typedArray.getDimensionPixelOffset(R.styleable.RecordView_stroke_width, SizeUtils.dp2px(5));
mDuration = typedArray.getInteger(R.styleable.RecordView_duration, 10);
mRadius = typedArray.getDimensionPixelOffset(R.styleable.RecordView_radius, SizeUtils.dp2px(40));
typedArray.recycle();
mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBgPaint.setStyle(Paint.Style.FILL);
mBgPaint.setColor(mBgColor);
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setColor(mStrokeColor);
mProgressPaint.setStrokeWidth(mStrokeWidth);
setEvent();
}
private void setEvent() {
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
mProgressValue++;
postInvalidate();
if (mProgressValue < mDuration*10) {
sendEmptyMessageDelayed(0, PROGRESS_INTERVAL);
} else {
finishRecord();
}
}
};
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
mStartRecordTime = System.currentTimeMillis();
handler.sendEmptyMessage(0);
}else if(event.getAction() == MotionEvent.ACTION_UP){
long duration = System.currentTimeMillis() - mStartRecordTime;
//
if(duration > ViewConfiguration.getLongPressTimeout()){
finishRecord();
}
handler.removeCallbacksAndMessages(null);
isRecording = false;
mStartRecordTime = 0;
mProgressValue = 0;
postInvalidate();
}
return false;
}
});
setOnClickListener(this);
setOnLongClickListener(this);
}
private void finishRecord() {
if(mOnRecordListener!=null){
mOnRecordListener.onFinish();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = w;
mArcRectF = new RectF(mStrokeWidth / 2f, mStrokeWidth / 2f,
mWidth - mStrokeWidth / 2f, mHeight - mStrokeWidth / 2f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, mBgPaint);
if (isRecording) {
canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius/10f*11, mBgPaint);
float sweepAngle = 360f * mProgressValue / (mDuration*10);
Log.i("sweepAngle",sweepAngle+"");
canvas.drawArc(mArcRectF, -90, sweepAngle, false, mProgressPaint);
}
}
@Override
public boolean onLongClick(View v) {
isRecording = true;
if(mOnRecordListener!=null){
mOnRecordListener.onRecordVideo();
}
return true;
}
@Override
public void onClick(View v) {
if(mOnRecordListener!=null){
mOnRecordListener.onTackPicture();
}
}
public interface OnRecordListener {
void onTackPicture();
void onRecordVideo();
void onFinish();
}
}
실현 도 매우 간단 합 니 다.먼저 원 을 그립 니 다.이 View 의 클릭 과 길 게 누 를 때 총 녹화 시간 과 현재 녹화 시간 에 따라 그 려 야 할 각 도 를 계산 하면 원 위 에서 진 도 를 그 릴 수 있 습 니 다.마지막 으로 인 터 페 이 스 를 통 해 길 게 누 르 고 녹화 가 완 료 된 사건 을 되 돌려 앞의 사진,녹화,녹화 가 완 료 된 코드 와 결합 하면 위의 효 과 를 완성 할 수 있다.
CameraView
앞의 초기 화가 간단 하지 않다 고 생각하면 CameraX 가 제공 하 는 CameraView 를 사용 할 수 있 습 니 다.이 안 에는 PreviewView,Preview,ImageCapture,VideoCapture 등 을 모두 봉 인 했 고 크기 조정,재단,회전 등 기능 도 실현 하여 사용 하기 가 더욱 간단 합 니 다.
우선 xml 파일 에 CameraView 추가
<androidx.camera.view.CameraView
android:id="@+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
그리고 Activity 에서 CameraView 를 실례 화하 여 현재 수명 주 기 를 직접 연결 하면 됩 니 다.
mBtnCameraSwitch = findViewById(R.id.camera_switch_button);
mCameraView.bindToLifecycle(this);
두 마디 만 하면 앞의 초기 작업 을 완성 할 수 있다.그리고 즐겁게 사진 을 찍 고 동 영상 을 녹화 할 수 있 습 니 다.사진 과 녹 화 된 코드 는 앞 과 마찬가지 로 모두 CamerView 대상 을 통 해
mCameraView.takePicture
,mCameraView.startRecording
을 호출 할 뿐 호출 하기 전에 mCameraView.setCaptureMode(CameraView.CaptureMode.IMAGE)
을 통 해 현재 모드 가 사진 인지 비디오 인지 전환 해 야 합 니 다.앞 에 있 는 사용자 정의 RecordView 를 레이아웃 파일 에 넣 고 CameraView 의 사진,비디오 코드 와 결합 하면 앞 과 같은 효 과 를 곧 실현 할 수 있 습 니 다.
사진 분석
CameraX 는 이미지 처리,컴퓨터 시각 또는 기계 학습 으로 추정 되 는 이미 지 를 CPU 로 접근 할 수 있 는 이미지 분석 기능 도 제공 합 니 다.빈 틈 없 이 버퍼 에 접근 할 수 있 으 며 일반적으로 사용 되 지 않 지만 기능 이 강 합 니 다.그림 분석 기 를 만 들 고 성명 주 기 를 연결 하면 됩 니 다.
mImageAnalysis = new ImageAnalysis.Builder()
.setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(rotation)
.build();
mImageAnalysis.setAnalyzer(mExecutorService, new ImageAnalysis.Analyzer() {
@Override
public void analyze(@NonNull ImageProxy image) {
}
});
cameraProvider.bindToLifecycle(CameraActivity.this,
cameraSelector,mPreview,mImageCapture,mVideoCapture,mImageAnalysis);
공급 자 확장공급 업 체 확장 프로그램:CameraX 는 외부 확장 API 를 제공 하여 핸드폰 생산 업 체 와 직접 연결 할 수 있 습 니 다.만약 에 이 핸드폰 업 체 가 CameraX 확장 프로그램 을 실현 하면 VamerX 의 확장 API 를 사용 하여 이러한 효 과 를 직접 호출 할 수 있 습 니 다.예 를 들 어 미 안,DHR,야간,자동 등 모델 입 니 다.
모든 휴대 전화 업 체 가 확장 프로그램 을 지원 하 는 것 은 아니 기 때문에 확장 프로그램 을 사용 할 때 이 휴대 전화 가 지원 하 는 지 판단 하고 추가 해 야 합 니 다.
미리 보기 인터페이스 에 외부 확장 을 설정 하려 면
Preview.Builder
과 CameraSelector cameraSelector)
두 개의 인자 가 필요 합 니 다.
private void setPreviewExtender(Preview.Builder builder, CameraSelector cameraSelector) {
AutoPreviewExtender extender = AutoPreviewExtender.create(builder);
if(extender.isExtensionAvailable(cameraSelector)){
extender.enableExtension(cameraSelector);
}
BokehPreviewExtender bokehPreviewExtender = BokehPreviewExtender.create(builder);
if(bokehPreviewExtender.isExtensionAvailable(cameraSelector)){
bokehPreviewExtender.enableExtension(cameraSelector);
}
HdrPreviewExtender hdrPreviewExtender = HdrPreviewExtender.create(builder);
if(hdrPreviewExtender.isExtensionAvailable(cameraSelector)){
hdrPreviewExtender.enableExtension(cameraSelector);
}
BeautyPreviewExtender beautyPreviewExtender = BeautyPreviewExtender.create(builder);
if(beautyPreviewExtender.isExtensionAvailable(cameraSelector)){
beautyPreviewExtender.enableExtension(cameraSelector);
}
NightPreviewExtender nightPreviewExtender = NightPreviewExtender.create(builder);
if(nightPreviewExtender.isExtensionAvailable(cameraSelector)){
nightPreviewExtender.enableExtension(cameraSelector);
}
}
촬영 한 사진 에 외부 확장 을 설정 하려 면 ImageCapture.Builder
과 CameraSelector cameraSelector)
두 개의 매개 변수 가 필요 합 니 다.
private void setImageCaptureExtender(ImageCapture.Builder builder, CameraSelector cameraSelector) {
AutoImageCaptureExtender autoImageCaptureExtender = AutoImageCaptureExtender.create(builder);
if (autoImageCaptureExtender.isExtensionAvailable(cameraSelector)) {
autoImageCaptureExtender.enableExtension(cameraSelector);
}
BokehImageCaptureExtender bokehImageCaptureExtender = BokehImageCaptureExtender.create(builder);
if(bokehImageCaptureExtender.isExtensionAvailable(cameraSelector)){
bokehImageCaptureExtender.enableExtension(cameraSelector);
}
HdrImageCaptureExtender hdrImageCaptureExtender = HdrImageCaptureExtender.create(builder);
if(hdrImageCaptureExtender.isExtensionAvailable(cameraSelector)){
hdrImageCaptureExtender.enableExtension(cameraSelector);
}
BeautyImageCaptureExtender beautyImageCaptureExtender = BeautyImageCaptureExtender.create(builder);
if(beautyImageCaptureExtender.isExtensionAvailable(cameraSelector)){
beautyImageCaptureExtender.enableExtension(cameraSelector);
}
NightImageCaptureExtender nightImageCaptureExtender = NightImageCaptureExtender.create(builder);
if(nightImageCaptureExtender.isExtensionAvailable(cameraSelector)){
nightImageCaptureExtender.enableExtension(cameraSelector);
}
}
demo 주소:주소 링크총결산
여기에 JetPack 의 CameraX 사용 완성 사진 과 동 영상 촬영 에 관 한 글 이 소개 되 었 습 니 다.더 많은 JetPack 은 CameraX 를 사용 하여 사진 을 찍 고 동 영상 을 찍 는 내용 에 대해 서 는 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!