首页 文章

即使设置了正确的尺寸,相机预览也会拉长

提问于
浏览
18

我正在创建一个相机应用程序,它实现了自己的相机预览拍照 . 该应用程序目前被强制进入纵向模式 .

我的问题是相机的预览稍微拉长(纵横比有点偏) . 有趣的是,我将SurfaceView大小设置为始终与预览大小匹配 . 这确保了纵横比应始终保持不变......但它不是......

这是我用来显示我的相机预览的布局:

public class Cp extends ViewGroup implements SurfaceHolder.Callback {
    private final String TAG = "CameraPreview";

    private boolean mPreviewRunning = false;

    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private Size mPreviewSize;
    private List<Size> mSupportedPreviewSizes;
    private Camera mCamera;

    public boolean IsPreviewRunning() {
        return mPreviewRunning;
    }

    public Cp(Context context) {
        this(context, null, 0);
    }

    public Cp(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Cp(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            requestLayout();
        }
    }

    public void switchCamera(Camera camera) {
        setCamera(camera);
        try {
            camera.setPreviewDisplay(mHolder);
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        requestLayout();

        camera.setParameters(parameters);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // We purposely disregard child measurements because act as a wrapper to
        // a SurfaceView that
        // centers the camera preview instead of stretching it.
        final int width = resolveSize(getSuggestedMinimumWidth(),
                widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(),
                heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes == null && mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters()
                    .getSupportedPreviewSizes();
        }
        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
                    height);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.height;
                previewHeight = mPreviewSize.width;
            }
            if (previewWidth == 0) {
                previewWidth = 1;
            }
            if (previewHeight == 0) {
                previewHeight = 1;
            }

            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height
                        / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width
                        / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2, width,
                        (height + scaledChildHeight) / 2);
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, acquire the camera and tell it where to
        // draw.
        try {
            if (mCamera != null) {
                Parameters params = mCamera.getParameters();
                mSupportedPreviewSizes = params.getSupportedPreviewSizes();
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        stop();
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null)
            return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mCamera != null) {
            // Now that the size is known, set up the camera parameters and
            // begin the preview.
            Camera.Parameters parameters = mCamera.getParameters();
            if (mPreviewSize != null) {
                parameters.setPreviewSize(mPreviewSize.width,
                        mPreviewSize.height);
            }
            requestLayout();

            mCamera.setParameters(parameters);
            mCamera.startPreview();
            mPreviewRunning = true;
        }
    }

    public void stop() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mPreviewRunning = false;
            mCamera = null;
        }
    }
}

请注意,在 onLayout 中交换宽度和高度,因为应用程序始终以纵向运行 .

我要包含一些图片,以显示问题所在:
enter image description here

enter image description here

我调试了代码 . 预览大小设置为1280x720,并且还正确调整布局大小以匹配该大小 .

我也尝试了不同的布局,但结果总是一样的......

3 回答

  • 2

    我遇到了同样的问题,在这个难题的几天后,我的Java类最终得到了这个代码:

    所以,问题出现了,因为相机显示器的尺寸(高x宽)为576x720,我的显示器为1184x720 . 因此,相机预览(我的表面视图类)被拉伸以填充父级 .

    因此,有效的方法是使这个视图比我的屏幕更大,并且只显示我的屏幕区域 . 因此,我不得不使用这种奇怪的帧组合(线性布局内的相对布局),以便相对的大小将达到我想要的大 - 表面视图将填充它 - 并且线性将只需要我希望展示的部分 .

    package com.example.frame.camera;
    
    import android.content.Context;
    import android.hardware.Camera;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.Toast;
    
    import com.example.frame.MainActivity;
    
    public class NativeCamera extends SurfaceView implements SurfaceHolder.Callback {
    
        static private NativeCamera instance;
    
        private LinearLayout        frame               = null;
        private RelativeLayout      innerFrame          = null;
    
        private Camera              camera;
        private final SurfaceHolder previewHolder;
        private static final String TAG                 = "NativeCamera.java";
    
        private boolean             inPreview           = false;
        private boolean             cameraConfigured    = false;
        private boolean             frontCamera         = false;
    
        private Camera.Size         size;
    
        static public NativeCamera getInstance() {
            if (NativeCamera.instance == null) {
                if (MainActivity.debug) {
                    Log.d(TAG, "Creating Camera Singleton");
                }
                NativeCamera.instance = new NativeCamera(MainActivity.instance);
            }
            return NativeCamera.instance;
        }
    
        public void onResume() {
            if (MainActivity.debug) {
                Log.d(TAG, "onResume");
            }
            camera = Camera.open();
            if (size != null) {
                initPreview(size.width, size.height);
            }
            startPreview();
        }
    
        public void onPause() {
            if (MainActivity.debug) {
                Log.d(TAG, "onPause");
            }
            if (inPreview) {
                camera.stopPreview();
            }
            camera.release();
            camera = null;
            inPreview = false;
        }
    
        public void onDestroy() {
            if (MainActivity.debug) {
                Log.d(TAG, "onDestroy");
            }
            NativeCamera.instance = null;
        }
    
        public void onSwitch() {
            frontCamera = !frontCamera;
            if (inPreview) {
                camera.stopPreview();
            }
            camera.release();
    
            int cam = frontCamera ? 1 : 0;
    
            camera = Camera.open(cam);
            if (size != null) {
                initPreview(size.width, size.height);
            }
            startPreview();
        }
    
        private NativeCamera(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
    
            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            previewHolder = getHolder();
            previewHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            // previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            innerFrame = new RelativeLayout(MainActivity.instance);
            innerFrame.addView(this);
            frame = new LinearLayout(MainActivity.instance);
            frame.addView(innerFrame);
        }
    
        public LinearLayout getFrame() {
            return frame;
        }
    
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            // TODO Auto-generated method stub
            if (MainActivity.debug) {
                Log.d(TAG, "surfaceChanged");
            }
            initPreview(width, height);
            startPreview();
        }
    
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            // no-op -- wait until surfaceChanged()
    
        }
    
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            // no-op
        }
    
        private Camera.Size getBestPreviewSize(int width, int height,
                Camera.Parameters parameters) {
            Camera.Size result = null;
    
            for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
                if (size.width <= width && size.height <= height) {
                    if (result == null) {
                        result = size;
                    } else {
                        int resultArea = result.width * result.height;
                        int newArea = size.width * size.height;
    
                        if (newArea > resultArea) {
                            result = size;
                        }
                    }
                }
            }
    
            this.size = result;
            return (result);
        }
    
        private void initPreview(int width, int height) {
            if (camera != null && previewHolder.getSurface() != null) {
    
                if (!cameraConfigured) {
                    Camera.Parameters parameters = camera.getParameters();
                    Camera.Size size = getBestPreviewSize(width, height, parameters);
    
                    if (size != null) {
                        parameters.setPreviewSize(size.width, size.height);
                        camera.setParameters(parameters);
                        cameraConfigured = true;
                        // Setting up correctly the view
                        double ratio = size.height / (double) size.width;
                        LayoutParams params = innerFrame.getLayoutParams();
                        params.height = MainActivity.size.y;
                        params.width = (int) (MainActivity.size.y * ratio);
                        innerFrame.setLayoutParams(params);
                        int deslocationX = (int) (params.width / 2.0 - MainActivity.size.x / 2.0);
                        innerFrame.animate().translationX(-deslocationX);
                    }
                }
    
                try {
                    camera.setPreviewDisplay(previewHolder);
                    camera.setDisplayOrientation(90);
    
                } catch (Throwable t) {
                    Log.e(TAG, "Exception in setPreviewDisplay()", t);
                    Toast.makeText(MainActivity.instance, t.getMessage(), Toast.LENGTH_LONG).show();
                }
    
            }
        }
    
        private void startPreview() {
            if (MainActivity.debug) {
                Log.d(TAG, "startPreview");
            }
            if (cameraConfigured && camera != null) {
                camera.startPreview();
                inPreview = true;
            }
        }
    
    }
    
  • 5

    我一直试图解决同样的问题......下面的代码对我有用:

    private void setMyPreviewSize(int width, int height) {
        // Get the set dimensions
        float newProportion = (float) width / (float) height;
    
        // Get the width of the screen
        int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
        int screenHeight = getWindowManager().getDefaultDisplay().getHeight();
        float screenProportion = (float) screenWidth / (float) screenHeight;
    
        // Get the SurfaceView layout parameters
        android.view.ViewGroup.LayoutParams lp = surfaceView.getLayoutParams();
        if (newProportion > screenProportion) {
            lp.width = screenWidth;
            lp.height = (int) ((float) screenWidth / newProportion );
        } else {
            lp.width = (int) (newProportion * (float) screenHeight);
            lp.height = screenHeight;
        }
        // Commit the layout parameters
        surfaceView.setLayoutParams(lp);
    }
    

    你可以看到线程:Resizing surface view for aspect ratio change in video display in android

  • 0

    这是一个解决方案

    surfaceChanged只是为了获得最佳的预览尺寸

    @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    Log.e("MYTAG", "surfaceChanged " );
    
                    Camera.Parameters myParameters = camera.getParameters();
                    Camera.Size myBestSize = getBestPreviewSize(width, height, myParameters);
    
                    if(myBestSize != null){
                        myParameters.setPreviewSize(myBestSize.width, myBestSize.height);
                        camera.setParameters(myParameters);
                        camera.startPreview();
    
    
                    }
                }
    
    
    
    @Override
        public void onPictureTaken(byte[] data, Camera camera) {
    
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
           imageReview.setImageBitmap(bitmap);
        }
    

    这将在imageReview上设置捕获的图像,但您需要一种方法来获取位图的旋转

    您需要为拉伸图像问题设置imageView scaleType属性

    <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            />
    

相关问题