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歩前進すると、それは突然めちゃくちゃに見えます。

私はこれを(完全なハックだと思う方法で)方向変更リスナーを使用して方向を1回だけ更新し、それ自体を無効にすることで解決しました。ビューが最初に設定された後にアクティブになるものが必要でした。私はおそらく他の場所でそれを行うことができたでしょう。

誰かがこれを完全に愚かにすることなくこれを修正する方法を知っているなら、助けてください!

/** 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;
}

要約すると、私が投稿した最初のバージョンは、NexusOneで正常に機能しました。しかし、それは私のSkyrocketでは機能しません。そこで動作させるには、2番目のバージョンを使用する必要があります。

4

1 に答える 1

0

不思議なことに、android:screenOrientation="landscape" を削除して画面を回転できるようにしても、同じ問題が発生しますが、電話を縦向きに回転すると縦向きに見えます。

まず、カメラ プレビューの向きはアクティビティの向きとは関係がないことに注意してください。どちらも違います。デバイスの向きを変更する場合はcamera.setDisplayOrientation、方向に従ってプレビューの向きを設定するために呼び出す必要があります。

この向きは度単位で設定する必要があります。これは、カメラ パービューでOrientationEventListenerを設定することで取得できます。ではonOrientationChanged(int orientation)、デバイスの正確な回転角度を度単位で取得します。

onOrientationChanged(int orientation)デバイスの向きのわずかな角度変化でもAPI が呼び出されるため、回転を計算することを忘れないでください。この質問を参照として見ることができます。

于 2012-11-22T04:29:28.757 に答える