A list,
Recently, CameraX released its first beta release, and the API madness is slowly leveling off compared to alpha. This article mainly covers CameraX’s simple photo saving, image analysis (for TWO-DIMENSIONAL code recognition and other purposes), zooming, focusing and other related content
Note:
- Used in this article
CameraX
Version for1.0.0 - beta01
. - To modify the camera proportion, switch the camera, qr code recognition and use the latest version, please click the link at the bottom to view the Demo.
Two, basic use
-
Gradle rely on
def camerax_version = "1.0.0 - beta01" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "Androidx. Camera: the camera - view: 1.0.0 - alpha08" implementation "androidx.camera:camera-lifecycle:${camerax_version}" Copy the code
-
XML layout
<! -... --> <androidx.camera.view.PreviewView android:id="@+id/view_finder" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <! -... --> Copy the code
-
Build the image capture use case
private void initImageCapture(a) { // Build the image capture case mImageCapture = new ImageCapture.Builder() .setFlashMode(ImageCapture.FLASH_MODE_AUTO) .setTargetAspectRatio(AspectRatio.RATIO_4_3) .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) .build(); // Rotate listener OrientationEventListener orientationEventListener = new OrientationEventListener((Context) this) { @Override public void onOrientationChanged(int orientation) { int rotation; // Monitors orientation values to determine the target rotation value if (orientation >= 45 && orientation < 135) { rotation = Surface.ROTATION_270; } else if (orientation >= 135 && orientation < 225) { rotation = Surface.ROTATION_180; } else if (orientation >= 225 && orientation < 315) { rotation = Surface.ROTATION_90; } else{ rotation = Surface.ROTATION_0; } mImageCapture.setTargetRotation(rotation); }}; orientationEventListener.enable(); }Copy the code
-
Build image analysis use cases (for TWO-DIMENSIONAL code recognition, etc.)
Note: The Analyzer back method will not fetch the next image without calling image.close()
private void initImageAnalysis(a) { mImageAnalysis = new ImageAnalysis.Builder() / / resolution .setTargetResolution(new Size(1280.720)) // Just send the latest image to the analyzer and discard it when it arrives. .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); mImageAnalysis.setAnalyzer(executor, image -> { int rotationDegrees = image.getImageInfo().getRotationDegrees(); LogUtils.e("Analysis#rotationDegrees", rotationDegrees); ImageProxy.PlaneProxy[] planes = image.getPlanes(); ByteBuffer buffer = planes[0].getBuffer(); / / to a byte [] // byte[] b = new byte[buffer.remaining()]; // LogUtils.e(b); // TODO:Turn off the image reference after analysis, otherwise it will block the generation of other images // image.close(); }); } Copy the code
-
Initializing the camera
privateExecutor executor; .private void initCamera(a) { executor = ContextCompat.getMainExecutor(this); cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // Bind preview bindPreview(cameraProvider); } catch (ExecutionException | InterruptedException e) { // No errors need to be handled for this Future. // This should never be reached. } }, executor); } Copy the code
-
Binding preview
private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) { Preview preview = new Preview.Builder() .build(); preview.setSurfaceProvider(mViewFinder.getPreviewSurfaceProvider()); CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build(); // The mImageCapture image capture case // mImageAnalysis Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mImageAnalysis, preview); mCameraInfo = camera.getCameraInfo(); mCameraControl = camera.getCameraControl(); initCameraListener(); } Copy the code
-
Take photos and save pictures
public void saveImage(a) { File file = new File(getExternalMediaDirs()[0], System.currentTimeMillis() + ".jpg"); ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build(); mImageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback() { @Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { String msg = "Image saved successfully:" + file.getAbsolutePath(); showMsg(msg); LogUtils.d(msg); Uri contentUri = Uri.fromFile(new File(file.getAbsolutePath())); Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, contentUri); sendBroadcast(mediaScanIntent); } @Override public void onError(@NonNull ImageCaptureException exception) { String msg = "Image save failed:"+ exception.getMessage(); showMsg(msg); LogUtils.e(msg); }}); }Copy the code
3, Custom PreviewView implementation gesture events (click, double click, zoom…)
-
Back off interface
public interface CustomTouchListener { /** * zoom in */ void zoom(a); /** * zoom out */ void ZoomOut(a); /** * click */ void click(float x, float y); /** * double click */ void doubleClick(float x, float y); /** ** long press */ void longClick(float x, float y); } Copy the code
-
Gesture monitor code
public class CameraXCustomPreviewView extends PreviewView { private GestureDetector mGestureDetector; /** * scaling related */ private float currentDistance = 0; private float lastDistance = 0; . Omit part of the constructorpublic CameraXCustomPreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mGestureDetector = new GestureDetector(context, onGestureListener); mGestureDetector.setOnDoubleTapListener(onDoubleTapListener); // mScaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener); // The long press screen cannot be dragged, but the long press event cannot be recognized // mGestureDetector.setIsLongpressEnabled(false); } @Override public boolean onTouchEvent(MotionEvent event) { / / over onTouchEvent return mGestureDetector.onTouchEvent(event); } GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { LogUtils.i("OnDown: press the"); return true; } @Override public void onShowPress(MotionEvent e) { LogUtils.i("OnShowPress: Just touched but not released."); } @Override public boolean onSingleTapUp(MotionEvent e) { LogUtils.i("OnSingleTapUp: Release immediately after a light touch."); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { LogUtils.i("OnScroll: Click and drag"); // More than two touch points if (e2.getPointerCount() >= 2) { Event.getx (0)/getY(0) is the location of the first touch float offSetX = e2.getX(0) - e2.getX(1); float offSetY = e2.getY(0) - e2.getY(1); // Use the trigonometric formula to calculate the distance between two points by calculating the difference between X and Y coordinates currentDistance = (float) Math.sqrt(offSetX * offSetX + offSetY * offSetY); if (lastDistance == 0) {// If it is the first time to judge lastDistance = currentDistance; } else { if (currentDistance - lastDistance > 10) { Enlarge / / if(mCustomTouchListener ! =null) { mCustomTouchListener.zoom(); }}else if (lastDistance - currentDistance > 10) { / / to narrow if(mCustomTouchListener ! =null) { mCustomTouchListener.ZoomOut(); }}}// After a scaling operation, assign the distance to lastDistance for the next judgment // But this method is written in the move action, which means that the finger has not been lifted, monitoring the change distance between the two fingers more than 10 // The zoom operation is performed, not judged by the distance change between two clicks // Therefore, this method of reserving the distance for the next judgment should not be used between two clicks lastDistance = currentDistance; } return true; } @Override public void onLongPress(MotionEvent e) { LogUtils.i("OnLongPress: Long press the screen"); if(mCustomTouchListener ! =null) { mCustomTouchListener.longClick(e.getX(), e.getY()); }}@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { LogUtils.i("OnFling: Slide and release"); currentDistance = 0; lastDistance = 0; return true; }}; GestureDetector.OnDoubleTapListener onDoubleTapListener =new GestureDetector.OnDoubleTapListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) { LogUtils.i("OnSingleTapConfirmed: Strict click"); if(mCustomTouchListener ! =null) { mCustomTouchListener.click(e.getX(), e.getY()); } return true; } @Override public boolean onDoubleTap(MotionEvent e) { LogUtils.i("OnDoubleTap: double click on the"); if(mCustomTouchListener ! =null) { mCustomTouchListener.doubleClick(e.getX(), e.getY()); } return true; } @Override public boolean onDoubleTapEvent(MotionEvent e) { LogUtils.i("OnDoubleTapEvent: Indicates that a double click has occurred."); return true; }}; }Copy the code
4. Double finger slide/double click zoom and click focus
private void initCameraListener(a) {
LiveData<ZoomState> zoomState = mCameraInfo.getZoomState();
float maxZoomRatio = zoomState.getValue().getMaxZoomRatio();
float minZoomRatio = zoomState.getValue().getMinZoomRatio();
LogUtils.e(maxZoomRatio);
LogUtils.e(minZoomRatio);
mViewFinder.setCustomTouchListener(new CameraXCustomPreviewView.CustomTouchListener() {
@Override
public void zoom(a) {
float zoomRatio = zoomState.getValue().getZoomRatio();
if (zoomRatio < maxZoomRatio) {
mCameraControl.setZoomRatio((float) (zoomRatio + 0.1)); }}@Override
public void ZoomOut(a) {
float zoomRatio = zoomState.getValue().getZoomRatio();
if (zoomRatio > minZoomRatio) {
mCameraControl.setZoomRatio((float) (zoomRatio - 0.1)); }}@Override
public void click(float x, float y) {
/ / 1.0.0 - beta08 createMeteringPointFactory () method has been deleted, Demo changed to use getMeteringPointFactory ()
MeteringPointFactory factory = mBinding.viewFinder.createMeteringPointFactory(mCameraSelector);
MeteringPoint point = factory.createPoint(x, y);
FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
// auto calling cancelFocusAndMetering in 3 seconds
.setAutoCancelDuration(3, TimeUnit.SECONDS)
.build();
mFocusView.startFocus(new Point((int) x, (int) y));
ListenableFuture future = mCameraControl.startFocusAndMetering(action);
future.addListener(() -> {
try {
FocusMeteringResult result = (FocusMeteringResult) future.get();
if (result.isFocusSuccessful()) {
mFocusView.onFocusSuccess();
} else{ mFocusView.onFocusFailed(); }}catch (Exception e) {
}
}, executor);
}
@Override
public void doubleClick(float x, float y) {
// Double-click to zoom in and out
float zoomRatio = zoomState.getValue().getZoomRatio();
if (zoomRatio > minZoomRatio) {
mCameraControl.setLinearZoom(0f);
} else {
mCameraControl.setLinearZoom(0.5 f); }}@Override
public void longClick(float x, float y) {}}); }Copy the code
5. Flash
switch (mImageCapture.getFlashMode()) {
case ImageCapture.FLASH_MODE_AUTO:
mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_ON);
mBtnLight.setText("Flash: On");
break;
case ImageCapture.FLASH_MODE_ON:
mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_OFF);
mBtnLight.setText("Flash: Off");
break;
case ImageCapture.FLASH_MODE_OFF:
mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);
mBtnLight.setText("Flash: Automatic");
break;
}
Copy the code
Sixth, the Demo
Demo:github.com/sdwfqin/And…