如果手机处于横向模式,则Android相机预览方向不正确

时间:2012-11-22 02:05:29

标签: android android-camera

我在这里发现了很多关于Android相机预览定位问题的问题。所有修复涉及将屏幕方向固定为横向或调用camera.setDisplayOrientation(90)或调用params.setRotation(90)。我无法找到实际上使其在横向方向上工作的设置组合。

使用android:screenOrientation =“landscape”将活动固定为横向模式。

我的问题是,如果我以纵向方向拿着相机并启动应用程序它工作正常(我的意思是它正确地将图像显示为风景)。如果我在启动应用程序时将相机保持在横向方向,则图像全部搞砸(有点隔行扫描)。如果我使用camera.setDisplayOrientation(90);图片不再混乱,但图像是侧向的。

奇怪的是,如果我删除android:screenOrientation =“landscape”并允许屏幕旋转,我仍然会遇到同样的问题,但如果我将手机旋转为肖像,它在纵向上看起来很好。如果我将它旋转回景观,那么它在景观中看起来很好!我唯一的问题是它首次启动时无法正常工作。

import java.util.List;

import android.content.Context;
import android.graphics.Color;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.util.Log;
import android.view.*;


public class CameraView extends SurfaceView
{    
    //Callback for the surfaceholder
    SurfaceHolder.Callback surfaceHolderListener = new SurfaceHolder.Callback() {
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera=Camera.open();
            } catch(Exception e) {
                Log.e("CameraView","Couldn't open the camera.",e);
            }
            try {
                camera.setPreviewDisplay(previewHolder);
            } catch (Throwable e) {
                Log.e("CameraView","Couldn't call setPreviewDisplay.",e);
            }
        }

        public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) {
            if(camera!=null) {
                Parameters params = camera.getParameters();

                List<Size> sizes = params.getSupportedPreviewSizes();
                Size optimalSize = getOptimalPreviewSize(sizes, w, h);
                params.setPreviewSize(optimalSize.width, optimalSize.height);

                camera.setParameters(params);
                camera.startPreview();
            }
        }

        public void surfaceDestroyed(SurfaceHolder arg0) {
            if(camera!=null) {
                camera.setPreviewCallback(null);
                camera.stopPreview();
                camera.release();
            }
        }

        private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
            final double ASPECT_TOLERANCE = 0.05;
            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;
        }
    };


    public CameraView(Context ctx) {
        super(ctx);

        previewHolder = this.getHolder();
        previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        previewHolder.addCallback(surfaceHolderListener);
        setBackgroundColor(Color.TRANSPARENT);
    }

    public CameraView(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);
    }

    private Camera camera;
    private SurfaceHolder previewHolder;
}

编辑: 真正的问题似乎是在surfaceChanged之后发生了一些事情。我发现如果我启动相机预览,然后在surfaceChanged的末尾添加一个调试点,相机看起来很好。然后,如果我向前迈出大约20步,它突然看起来搞砸了。

我通过使用方向更改侦听器一次更新方向然后禁用自身来解决这个问题(我认为这是一个完全的黑客攻击)。我只需要在最初设置视图后激活的东西。我可能已经在其他地方做过了。

如果有人知道如何解决这个问题而不是完全愚蠢,请帮忙!

/** This works fine for me, but it's a hack. */

import java.util.List;

import android.content.Context;
import android.graphics.Color;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView3
    extends SurfaceView
{
    private static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.05;
        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;
    }


    private class CameraSurfaceHolder
        extends OrientationEventListener
        implements SurfaceHolder.Callback
    {
        CameraSurfaceHolder(Context ctx) {
            super(ctx);
        }

        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera= Camera.open();
            } catch(Exception e) {
                Log.e("CameraView","Couldn't open the camera.",e);
            }
            try {
                camera.setPreviewDisplay(previewHolder);
            } catch (Throwable e) {
                Log.e("CameraView","Couldn't call setPreviewDisplay.",e);
            }
        }

        public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) {
        }

        public void surfaceDestroyed(SurfaceHolder arg0) {
            if(camera!=null) {
                camera.setPreviewCallback(null);
                camera.stopPreview();
                camera.release();
            }
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if(camera!=null) {
                DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
                camera.stopPreview();
                CameraView3.this.layout(0, 0, dm.widthPixels-1, dm.heightPixels-1);
                camera.startPreview();
                disable();
                return;
            }
        }
    }


    public CameraView3(Context ctx) {
        super(ctx);
        this.ctx = ctx;

        previewHolder = this.getHolder();
        previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolderListener = new CameraSurfaceHolder(ctx);
        previewHolder.addCallback(surfaceHolderListener);
        surfaceHolderListener.enable();

        setBackgroundColor(Color.TRANSPARENT);
    }

    private Context ctx;
    private Camera camera;
    private SurfaceHolder previewHolder;
    private CameraSurfaceHolder surfaceHolderListener;
    private Size optimalSize;
}

总之,我发布的第一个版本在我的Nexus One上运行良好。但是,它不适用于我的Skyrocket。我需要使用第二个版本才能在那里工作。

1 个答案:

答案 0 :(得分:0)

  

奇怪的是,如果我删除android:screenOrientation =“landscape”和   让屏幕旋转,我仍然有同样的问题,但如果我   将手机旋转为肖像,画像看起来很好。

首先请注意,相机预览方向与活动方向无关。两者都不同。当您更改设备方向时,您必须调用camera.setDisplayOrientation根据方向设置预览的方向。

此方向必须以度数设置。您可以在相机视图中设置OrientationEventListener。在onOrientationChanged(int orientation)中,您将获得设备的精确旋转角度(以度为单位)。

不要忘记计算旋转,因为即使设备方向的角度变化很小,也会调用API onOrientationChanged(int orientation)。您可以将this问题视为参考。