安卓 - 相机预览侧着显示

126

我正在使用一个预览来显示相机在屏幕上看到的内容。

我能够顺利地获取所有工作,创建了表面、设置了表面,并且该表面被显示出来。

但是在竖屏模式下它总是以不正确的90度角度显示图片。

就像这张图片一样:

alt text

我知道使用以下代码会将图片设置为正常显示:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

然而,我在一个包含其他元素的Activity中使用了预览,这并不适合以横屏模式显示我的Activity(默认情况下被禁用)。

所以我想知道是否有办法只改变预览的方向?并让剩下的Activity正确地以竖屏模式显示?

或者有没有一种方法可以旋转预览,以使其正确显示?


请看这个链接 - https://dev59.com/g2LVa4cB1Zd3GeqP1fYj#10259572 - Suvam Roy
8个回答

149

这个问题最初似乎是与某些硬件有关的一个漏洞,可以在此处查看,但可以通过使用API 8中可用的调用mCamera.setDisplayOrientation(degrees)来解决。所以我是这样实现的:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {            
    if (isPreviewRunning) {
        mCamera.stopPreview();
    }

    Parameters parameters = mCamera.getParameters();
    Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

    if(display.getRotation() == Surface.ROTATION_0) {
        parameters.setPreviewSize(height, width);                           
        mCamera.setDisplayOrientation(90);
    }

    if(display.getRotation() == Surface.ROTATION_90) {
        parameters.setPreviewSize(width, height);                           
    }

    if(display.getRotation() == Surface.ROTATION_180) {
        parameters.setPreviewSize(height, width);               
    }

    if(display.getRotation() == Surface.ROTATION_270) {
        parameters.setPreviewSize(width, height);
        mCamera.setDisplayOrientation(180);
    }

    mCamera.setParameters(parameters);
    previewCamera();                      
}

And the previewCamera method :

public void previewCamera() {        
    try {           
        mCamera.setPreviewDisplay(mSurfaceHolder);          
        mCamera.startPreview();
        isPreviewRunning = true;
    } catch(Exception e) {
        Log.d(APP_CLASS, "Cannot start preview", e);    
    }
}

这是在HTC Desire手机上的情况。我最初必须在每个旋转检查中放置日志语句以说明旋转是什么,并在设备上调试,同时观察logCat输出,当我旋转设备时进行调试。对于HTC Desire,0是您所期望的手机纵向模式(portrait),90度是将手机逆时针旋转90度(我原本以为它会顺时针旋转)。在代码中,当手机处于90度或180度时,我没有需要执行任何显示旋转操作——设备似乎可以自己处理。唯一一个不正常工作的点:当您将设备顺时针旋转270度并且显示屏幕已经相应地反向旋转时,如果您将设备逆时针旋转270度,则无法正确补偿。

P.S. 请注意,在适当的旋转中,宽度和高度发生了交换。


7
但是 setDisplayOrientation(degree) 方法支持2.2及以上版本,那么低版本怎么办?parameters.setRotation(90); parameters.set("orientation", "portrait"); 不起作用。如果你有任何解决低版本的方案,请帮助我。 - Vikram
1
我在我的应用程序中实现了一个预览,它将始终显示为纵向模式。我一直将屏幕旋转90度,这似乎在每个设备上都可以使用,直到我们在HTC Desire C上测试时出现了问题。由于我不能依靠该设备来进行测试,因此我希望您能澄清一下您建议的修复是否在HTC Desire上正常工作。谢谢! - argenkiwi
13
因为表面尺寸不是我的手机的有效预览尺寸(可能是因为我保持状态栏可见),所以“mCamera.setParameters(parameters);”语句会导致我的应用程序崩溃。然而,我发现使用“mCamera.setDisplayOrientation(90)”然后使用“mCamera.setPreviewDisplay(mSurfaceHolder);”而不设置参数也可以工作! - nicopico
3
使用 switch 语句会让代码更加清晰。 - Siavash
2
这是否假设预览在所有设备上都是横向的?因为在某些设备上它是横向的,在其他设备上则是竖直的...有没有一种方法可以检查设备的默认相机方向是否与手机的纵向一致? - Siavash
显示剩余6条评论

16

尝试设置显示方向。这解决了我的问题。

 mCamera.setDisplayOrientation(90);

5
保存时以横向方式保存肖像图像。有解决方法吗? - Akanksha Rathore
@Akanksha:这个标志只适用于预览显示。它不会改变在onPreviewFrame()onPictureTaken()中返回的缓冲区方向。 - Alex Cohn

13
 public void surfaceCreated(SurfaceHolder holder) {
     mCamera = Camera.open();
     mCamera.setDisplayOrientation(90);
     try {
         mCamera.setPreviewDisplay(holder);
         mCamera.setPreviewCallback(new PreviewCallback() {

             @Override
             public void onPreviewFrame(byte[] data, Camera camera) {
             }
         });

     } catch (Exception e) {
         e.printStackTrace();
     }
}

尝试这段代码


3
保存时以横向方式保存肖像图像。有解决方法吗? - Akanksha Rathore
1
@Akanksha 这可能与EXIF参数有关。 - EpicPandaForce

4

我遇到了前置摄像头倒置的问题。然后我使用了Android文档中记录的以下方法 - setDisplayOrientation(int).

public void setCameraDisplayOrientation(Activity activity , int icameraId , Camera camera1s)
    {
        CameraInfo cameraInfo = new CameraInfo();

        Camera.getCameraInfo(icameraId, cameraInfo);

        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();

        int degrees = 0; // k

        switch (rotation)
        {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;

        }

        int result;

        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
        {
            // cameraType=CAMERATYPE.FRONT;

            result = (cameraInfo.orientation + degrees) % 360;
            result = (360 - result) % 360; // compensate the mirror

        }
        else
        { // back-facing

            result = (cameraInfo.orientation - degrees + 360) % 360;

        }
        // displayRotate=result;
        camera.setDisplayOrientation(result);


    }

3
这个方法来源于相机文档:http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)。 - VinceFior
@VinceFior 如果他从官方文件中发布,有什么问题吗? - Ranjithkumar
1
@RanjithKumar 没有特别的意思,我只是想给出处并引导人们去了解更多背景。 :) - VinceFior

4

我采用mCamera.setDisplayOrientation(90)的建议进行操作,但由于某些原因,其他方法在2.3.3版本中不起作用,所以我还旋转了位图。

旋转位图的方法如下:

Matrix matrix = new Matrix();
matrix.postRotate(90);
imageView1 = new ImageView(this);
Bitmap bitmap = BitmapFactory.decodeFile(files[i].getAbsolutePath());
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(rotatedBitmap, 80, 80, true);
imageView1.setImageBitmap(scaledBitmap);

0
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.

    if (mHolder.getSurface() == null) {
        // preview surface does not exist
        return;
    }

    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        e.printStackTrace();
    }

    Camera.Parameters parameters = mCamera.getParameters();
    Display display = ((WindowManager) getContext().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

    if (display.getRotation() == Surface.ROTATION_0) {
        parameters.setPreviewSize(h, w);
        mCamera.setDisplayOrientation(90);
    }

    if (display.getRotation() == Surface.ROTATION_90) {
        parameters.setPreviewSize(w, h);
        mCamera.setDisplayOrientation(0);
    }

    if (display.getRotation() == Surface.ROTATION_180) {
        parameters.setPreviewSize(h, w);
        mCamera.setDisplayOrientation(270);
    }

    if (display.getRotation() == Surface.ROTATION_270) {
        parameters.setPreviewSize(w, h);
        mCamera.setDisplayOrientation(180);
    }

    previewCamera();
}

public void previewCamera() {
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    } catch (Exception e) {
        //Log.d(APP_CLASS, "Cannot start preview", e);
        e.printStackTrace();
    }
}

0
我已经将我的代码与教程中的代码进行了比较,最终解决问题的方法是将以下代码放入我的AndroidManifest.xml文件中: 在<activity>标签中:
android:screenOrientation="landscape"
android:configChanges="keyboardHidden|orientation">

0

我认为SENSOR_ORIENTATION的值将解释应该使用哪个值进行旋转,而不是硬编码为90度。

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        if (manager == null) {
            Log.i(TAG, "camera manager is null");
            return;
        }
        for (String id: manager.getCameraIdList()) {
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
            Integer orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
            Log.i(TAG, "camera sensor orientation is " + orientation);
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接