0

I am using camera2 API for my project. The preview is set on Google's recommended AutofitTextureView.
But using their code, the preview was stretched when the textureview filled the whole screen.
So I found a stackoverflow answer and edited the code to this:

AutoFitTextureView.java:

package com.example.android.camera2basic;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.TextureView;
    
    public class AutoFitTextureView extends TextureView {
    
        private int mRatioWidth = 0;
        private int mRatioHeight = 0;
    
        public AutoFitTextureView(Context context) {
            this(context, null);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void setAspectRatio(int width, int height) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException("Size cannot be negative.");
            }
            mRatioWidth = width;
            mRatioHeight = height;
            requestLayout();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            if (0 == mRatioWidth || 0 == mRatioHeight) {
                setMeasuredDimension(width, height);
            } else {
    
                #This is the line that I have changed:
                if (width > height * mRatioWidth / mRatioHeight) {
    
                    setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
                } else {
                    setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
                }
            }
        }
    
    }

Using the above code and this code:

private static final int MAX_PREVIEW_WIDTH = 1920;
private static final int MAX_PREVIEW_HEIGHT = 1080;
private Size previewSize;
            int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
            int mSensorOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
            boolean swappedDimensions = false;
            switch (displayRotation) {
                case Surface.ROTATION_0:
                case Surface.ROTATION_180:
                    if (mSensorOrientation == 90 || mSensorOrientation == 270) {
                        swappedDimensions = true;
                    }
                    break;
                case Surface.ROTATION_90:
                case Surface.ROTATION_270:
                    if (mSensorOrientation == 0 || mSensorOrientation == 180) {
                        swappedDimensions = true;
                    }
                    break;
                default:
                    //Log.e(TAG, "Display rotation is invalid: " + displayRotation);
            }
            Point displaySize = new Point();
            getWindowManager().getDefaultDisplay().getSize(displaySize);
            int rotatedPreviewWidth = width;
            int rotatedPreviewHeight = height;
            int maxPreviewWidth = displaySize.x;
            int maxPreviewHeight = displaySize.y;
    
            if (swappedDimensions) {
                rotatedPreviewWidth = height;
                rotatedPreviewHeight = width;
                maxPreviewWidth = displaySize.y;
                maxPreviewHeight = displaySize.x;
            }
    
            if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
                maxPreviewWidth = MAX_PREVIEW_WIDTH;
            }
    
            if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
                maxPreviewHeight = MAX_PREVIEW_HEIGHT;
            }
    
Size largest = Collections.max(Arrays.asList(map.getOutputSizes(SurfaceTexture.class)), new PrismaCamera.CompareSizesByArea());
            previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 
            rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest);
    
    
    public static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
    
            // Collect the supported resolutions that are at least as big as the preview Surface
            List<Size> bigEnough = new ArrayList<>();
            // Collect the supported resolutions that are smaller than the preview Surface
            List<Size> notBigEnough = new ArrayList<>();
            int w = aspectRatio.getWidth();
            int h = aspectRatio.getHeight();
            for (Size option : choices) {
                if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
                        option.getHeight() == option.getWidth() * h / w) {
                    if (option.getWidth() >= textureViewWidth &&
                            option.getHeight() >= textureViewHeight) {
                        bigEnough.add(option);
                    } else {
                        notBigEnough.add(option);
                    }
                }
            }
    
            // Pick the smallest of those big enough. If there is no one big enough, pick the
            // largest of those not big enough.
            if (bigEnough.size() > 0) {
                return Collections.min(bigEnough, new CompareSizesByArea());
            } else if (notBigEnough.size() > 0) {
                return Collections.max(notBigEnough, new CompareSizesByArea());
            } else {
                Log.e(TAG, "Couldn't find any suitable preview size");
                return choices[0];
            }
        }

I was able to get the camera preview to full screen, without stretching the preview. But the problem is that when the width and height of the TextureView is set to a value other than match_parent, the camera preview still fills the whole screen.

For example, This:

<com.camera.AutoFitTextureView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/textureView"
        />

and this:

<com.camera.AutoFitTextureView
        android:layout_width="500dp"
        android:layout_height="500dp"
        android:id="@+id/textureView"
        />

sets the preview to full screen. What I want is that the camera preview should fit perfectly to the width and height of the textureview, without taking the whole space and filling the screen. How to achieve this? The camera preview should be perfect in 9:16, 1:1, 3:4 and all the other ratios.
Is this possible? Waiting for your answer. Regards.

Update: Here is my current layout file:


    <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:background="@android:color/black">

    <TextureView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/textureView"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />


</androidx.constraintlayout.widget.ConstraintLayout>

WebDiva
  • 133
  • 3
  • 23
  • You can set fixed size for a `TextureView` and you don't need the `AutoFitTextureView` for that. You probably want to prepare separate layouts for `9:16` and `1:1`. `AutoFitTextureView` was invented for a purpose: to easily fill the screens of devices with very different form factors, even when they don't match the camera aspect ratio. – Alex Cohn Oct 06 '20 at 15:20
  • 1
    Thank you. I used the same code and used TextureView in the layout file instead of AutoFitTextureView but the problem is that now the preview is getting stretched. Should i remove any of the code from above? – WebDiva Oct 06 '20 at 15:46
  • Please show your layout xml. The easiest is to put the texture [inside `ConstraintLayout`](https://stackoverflow.com/a/45964536/192373) – Alex Cohn Oct 06 '20 at 17:03
  • 1
    Thank you. I have edited the layout as per your instructions but the issue persists. Should I remove any of the Java code, or make any changes to it? – WebDiva Oct 07 '20 at 06:39
  • In your updated layout, you fill the screen with `textureView`; this is exactly what the **AutoFitTextureView** was introduced for, but this is not what you wanted in the first place. You asked to provide some predefined area for the viewfinder, e.g. fit it inside a small box, didn't you? – Alex Cohn Oct 07 '20 at 07:01
  • 1
    No I meant that the camera preview should fit perfectly in every width and height. For example, in the native camera app, the camera preview could be a square, it could be fullscreen etc. This is what I want to achieve. Hope that made it clear :). – WebDiva Oct 07 '20 at 07:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222659/discussion-between-alex-cohn-and-webdiva). – Alex Cohn Oct 07 '20 at 11:51

0 Answers0