preview stretches in camera2 apis

Below answer assumes you are in portrait mode only.

Your question is

How to use the preview in full-screen without stretching it

Let's break it down to 2 things:

  1. You want the preview to fill the screen
  2. The preview cannot be distorted

First you need to know that this is logically impossible without crop, if your device's viewport has a different aspect ratio with any available resolution the camera provides.

So I would assume you accept cropping the preview.

Step 1: Get a list of available resolutions

StreamConfigurationMap map = mCameraCharacteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
    throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);

Now you get a list of available resolutions (Sizes) of your device's camera.

Step 2: Find the best aspect ratio

The idea is to loop the sizes and see which one best fits. You probably need to write your own implementation of "best fits".

I am not going to provide any code here since what I have is quite different from your use case. But ideally, it should be something like this:

Size findBestSize (Size[] sizes) {
    //Logic goes here
}

Step 3: Tell the Camera API that you want to use this size

    //...
    textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
    Surface surface = textureView.getSurface();
    try {
        mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mPreviewRequestBuilder.addTarget(surface);
        mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                mSessionCallback, null);
    } catch (final Exception e) {
        //...
    }

Step 4: Make your preview extends beyond your viewport

This is then nothing related to the Camera2 API. We "crop" the preview by letting the SurfaceView / TextureView extends beyond device's viewport.

First place your SurfaceView or TextureView in a RelativeLayout.

Use the below to extend it beyond the screen, after you get the aspect ratio from step 2.
Note that in this case you probably need to know this aspect ratio before you even start the camera.

   //Suppose this value is obtained from Step 2.
    //I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
    float cameraAspectRatio = (float) 0.75;

    //Preparation
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int screenWidth = metrics.widthPixels;
    int screenHeight = metrics.heightPixels;
    int finalWidth = screenWidth;
    int finalHeight = screenHeight;
    int widthDifference = 0;
    int heightDifference = 0;
    float screenAspectRatio = (float) screenWidth / screenHeight;

    //Determines whether we crop width or crop height
    if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
        finalHeight = (int) (screenWidth / cameraAspectRatio);
        heightDifference = finalHeight - screenHeight;
    } else { //Keep height crop width
        finalWidth = (int) (screenHeight * cameraAspectRatio);
        widthDifference = finalWidth - screenWidth;
    }

    //Apply the result to the Preview
    RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
    lp.width = finalWidth;
    lp.height = finalHeight;
    //Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
    lp.leftMargin = - (widthDifference / 2);
    lp.topMargin = - (heightDifference / 2);
    cameraView.setLayoutParams(lp);

If you don't care about the result of Step 2, you can actually ignore Step 1 to Step 3 and simply use a library out there, as long as you can configure its aspect ratio. (It looks like this one is the best, but I haven't tried yet)

I have tested using my forked library. Without modifying any code of my library, I managed to make the preview fullscreen just by using Step 4:

Before using Step 4:
enter image description here

After using Step 4:
enter image description here

And the preview just after taking a photo will not distort as well, because the preview is also extending beyond your screen.
But the output image will include area that you cannot see in the preview, which makes perfect sense.

The code of Step 1 to Step 3 are generally referenced from Google's CameraView.


That's a common problem on some devices. I've noticed it mostly on samsung. You may use a trick with setting transformation on your TextureView to make it centerCrop like ImageView behaviour