3

我在这里发现了很多关于 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。我需要使用第二个版本让它在那里工作。

4

1 回答 1

0

奇怪的是,如果我删除 android:screenOrientation="landscape" 并允许屏幕旋转,我仍然会遇到同样的问题,但是如果我将手机旋转到纵向,纵向看起来很好。

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

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

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

于 2012-11-22T04:29:28.757 回答