How to get an Android Camera2 with 1:1 ratio like Instagram?

前端 未结 4 1638
渐次进展
渐次进展 2021-02-13 14:21

My question is very simple:

How to get an Android android.hardware.Camera2 with 1:1 ratio and without deformation like Instagram?

相关标签:
4条回答
  • 2021-02-13 14:52

    Thanks @CommonsWare.

    I followed your advice using negative margin (top and bottom) and it works.

    To do that, I just update AutoFitTextureView the GoogeSamples project android-Camera2Basic this way:

    public class AutoFitTextureView extends TextureView {
    
        //...
        private boolean mWithMargin = false;
    
        //...
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, widthMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            int margin = (height - width) / 2;
    
            if(!mWithMargin) {
                mWithMargin = true;
                ViewGroup.MarginLayoutParams margins = ViewGroup.MarginLayoutParams.class.cast(getLayoutParams());
                margins.topMargin = -margin;
                margins.bottomMargin = -margin;
                margins.leftMargin = 0;
                margins.rightMargin = 0;
                setLayoutParams(margins);
            }
    
            if (0 == mRatioWidth || 0 == mRatioHeight) {
                setMeasuredDimension(width, height);
            } else {
                if (width < height) {
                    setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
                } else {
                    setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
                }
            }
        }
    }
    

    0 讨论(0)
  • 2021-02-13 14:55

    For anybody looking for this, I tried the above answer. Adding a margin to hide part of textureview to make it look square looks good in preview. But when saving the image, you should remove the hidden areas from the output image also.

    An Easier solution is to show a full textureview and to overlay some other layouts on it to make it look square.You can easily crop the image from output.

    you can find the sample code here

    0 讨论(0)
  • 2021-02-13 15:01

    Create custom texture view like this:

    public class AutoFitTextureView extends TextureView {
    
        private int mCameraWidth = 0;
        private int mCameraHeight = 0;
        private boolean mSquarePreview = false;
    
        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, boolean squarePreview) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException("Size cannot be negative.");
            }
            mCameraWidth = width;
            mCameraHeight = height;
            mSquarePreview = squarePreview;
            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 == mCameraWidth || 0 == mCameraHeight) {
                setMeasuredDimension(width, height);
            } else {
                /**
                 * Vertical orientation
                 */
                if (width < height) {
                    if (mSquarePreview) {
                        setTransform(squareTransform(width, height));
                        setMeasuredDimension(width, width);
                    } else {
                        setMeasuredDimension(width, width * mCameraHeight / mCameraWidth);
                    }
                }
                /**
                 * Horizontal orientation
                 */
                else {
                    if (mSquarePreview) {
                        setTransform(squareTransform(width, height));
                        setMeasuredDimension(height, height);
                    } else {
                        setMeasuredDimension(height * mCameraWidth / mCameraHeight, height);
                    }
                }
            }
        }
    
        private Matrix setupTransform(int sw, int sh, int dw, int dh) {
            Matrix matrix = new Matrix();
            RectF src = new RectF(0, 0, sw, sh);
            RectF dst = new RectF(0, 0, dw, dh);
            RectF screen = new RectF(0, 0, dw, dh);
    
            matrix.postRotate(-90, screen.centerX(), screen.centerY());
            matrix.mapRect(dst);
    
            matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
            matrix.mapRect(src);
    
            matrix.setRectToRect(screen, src, Matrix.ScaleToFit.FILL);
            matrix.postRotate(-90, screen.centerX(), screen.centerY());
    
            return matrix;
        }
    
        private Matrix squareTransform(int viewWidth, int viewHeight) {
            Matrix matrix = new Matrix();
    
            if (viewWidth < viewHeight) {
                MyLogger.log(AutoFitTextureView.class, "Horizontal");
                matrix.setScale(1, (float) mCameraHeight / (float) mCameraWidth, viewWidth / 2, viewHeight / 2);
            } else {
                MyLogger.log(AutoFitTextureView.class, "Vertical");
                matrix.setScale((float) mCameraHeight / (float) mCameraWidth, 1, viewWidth / 2, viewHeight / 2);
            }
    
            return matrix;
        }
    }
    

    And call setAspectRatio for your texture view in activity/fragment.

    if (mVideoSize.width > mVideoSize.height) {
        mTextureView.setAspectRatio(mVideoSize.height, mVideoSize.width, true);
    } else {
        mTextureView.setAspectRatio(mVideoSize.width, mVideoSize.height, true);
    }
    mCamera.setPreviewTexture(mTextureView.getSurfaceTexture());
    mCamera.startPreview();
    
    0 讨论(0)
  • 2021-02-13 15:01

    I did it with the Layout, in that way, google code can be keeped as it comes and automatically set a 1:1 preview based on the UI.

        <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/footer"
        android:layout_below="@id/header">
    
        <com.example.android.camera2video.AutoFitTextureView
            android:id="@+id/texture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintDimensionRatio="w,1:1"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    
    </android.support.constraint.ConstraintLayout>
    

    Just put the AutoFitTextureView inside a ConstraintLayout and then

    previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height, videoSize); does all the magic

    0 讨论(0)
提交回复
热议问题