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:

  1. Used in this articleCameraXVersion for1.0.0 - beta01.
  2. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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…)

  1. 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
  2. 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…