纵向模式下的相机setDisplayOrientation()打破纵横比

时间:2013-06-15 18:22:55

标签: android android-camera

我正在尝试让相机预览在纵向模式下正常工作,其中活动本身可以正常更改方向(即,不会锁定到横向)。

setDisplayOrientation()的使用严重打破了预览的行为。

谷歌自己的ApiDemos可以证明这一点。第一张图片基于CameraPreview示例项目的android-17版本中的ApiDemos,其中我唯一的更改是从此活动的条目中移除android:orientation="landscape"表现。下图是运行Android 4.2的Nexus 4显示屏上显示的内容的屏幕截图,相机指向8.5“方形纸张:

Nexus 4 ApiDemos Preview, Portrait Mode

预览中不明显的是它旋转了90度。你在图像最右边看到的袜子包裹的脚实际上在广场下面。

至少在横向模式下,解决方法是使用setDisplayOrientation()。但是,如果您将Preview CameraPreview的内部类修改为mCamera.setDisplayOrientation(90);,则可以获得此结果:

Nexus 4 ApiDemos Preview, Portrait Mode, with setDisplayOrientation(90)

值得注意的是,广场不再是正方形。

它使用与以前相同的SurfaceView大小。我已使用ApiDemos之外的其他代码重现了此方案,并且我在该代码中使用TextureView获得了相同的行为。

我简单地想到问题可能是getSupportedPreviewSizes()可能会根据setDisplayOrientation()返回不同的值,但快速测试表明情况并非如此。

任何人都知道如何让setDisplayOrientation()在纵向模式下工作而不会将图像的宽高比推到预览位置?

谢谢!

3 个答案:

答案 0 :(得分:15)

好的,我想我想出来了。众所周知的谜题有几个部分,我感谢@kcoppock帮助我解决其中一些问题的见解。

首先,在确定要选择的预览尺寸时,需要考虑方向。例如,来自getOptimalPreviewSize()的{​​{1}}的{​​{1}}无法定位,只是因为他们的应用版本的方向已锁定为横向。如果您希望方向浮动,则需要反转目标纵横比以匹配。那么,CameraPreview有:

ApiDemos

你还需要:

getOptimalPreviewSize()

其中double targetRatio=(double)width / height; 是一个从0到360的值,我从大约100行的一些严重丑陋的代码中确定,这就是为什么我将所有这些包装在一个我将很快发布的可重用组件。该代码基于the setDisplayOrientation() JavaDocsthis StackOverflow answer

(顺便说一句,如果您在2013年7月1日之后阅读此内容,并且您在该答案中没有看到该组件的链接,请发表评论以提醒我,因为我忘了)

其次,在控制您正在使用的if (displayOrientation == 90 || displayOrientation == 270) { targetRatio=(double)height / width; } / displayOrientation的宽高比时,您需要考虑该显示方向。来自SurfaceView的{​​{1}}活动有自己的TextureView CameraPreview来处理宽高比,您需要在纵向中反转宽高比:

ApiDemos

其中Preview是相同的值(ViewGroup if (displayOrientation == 90 || displayOrientation == 270) { previewWidth=mPreviewSize.height; previewHeight=mPreviewSize.width; } else { previewWidth=mPreviewSize.width; previewHeight=mPreviewSize.height; } 分别是纵向和反向肖像,并注意我没有尝试过反向纵向或反向横向工作,所以可能需要更多的调整。)

第三 - 我发现真气 - 你必须在displayOrientation上致电90之前开始预览。否则,就好像图片的宽高比被应用于预览帧一样,搞砸了。

所以我以前的代码类似于以下内容:

270

那是错的。你真正需要的是:

setPictureSize()

如果没有这种变化,即使在风景中,我也会得到拉伸的纵横比。对于Camera.Parameters Camera.Parameters parameters=camera.getParameters(); Camera.Size pictureSize=getHost().getPictureSize(parameters); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPictureFormat(ImageFormat.JPEG); camera.setParameters(parameters); camera.startPreview(); 而言,这不是问题,因为他们没有拍照,因此他们从未调用Camera.Parameters parameters=camera.getParameters(); Camera.Size pictureSize=getHost().getPictureSize(parameters); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); camera.setParameters(parameters); camera.startPreview(); parameters=camera.getParameters(); parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPictureFormat(ImageFormat.JPEG); camera.setParameters(parameters);

但是,到目前为止,在Nexus 4上,我现在拥有纵向和横向的纵横比,具有浮动方向(即未锁定到横向)。

如果我遇到其他设备需要的其他黑客攻击,并且在发布我的CameraPreview / ApiDemos组件时,我会尽量记住修改此问题。


更新#1

当然,很可能几乎这个简单。 : - )

解决问题,setPictureSize()搞砸了宽高比......直到你拍照。此时,预览会切换到错误的宽高比。

一种可能性是将图片限制为与预览具有相同(或非常接近)宽高比的图片,因此不存在打嗝。我不喜欢这个答案,因为用户应该能够获得相机提供的任何图片大小。

您可以通过以下方式限制损害范围:

  • 仅在拍摄照片之前更新CameraView的照片尺寸等
  • 拍摄照片后,恢复拍摄前预拍CameraFragment

在拍照时,这仍然会出现错误的宽高比。对于这个问题的长期解决方案 - 我认为 - 将在拍摄照片时暂时用静止图像(最后一个预览帧)替换相机预览。我最终会试试这个。


更新#2 my CWAC-Camera library的v0.0.1现已推出,其中包含上述代码。

答案 1 :(得分:1)

好的伙计们, 关于这个f * * *问题,过去两周我真的很疯狂。 好吧,很少有事情要知道:

  • 有时你只是获得黑色边框,如果操作栏或软按钮可见,只在某些设备上可用,我的建议: * 不关心< / em> *或者你会变得疯狂,或者削减预览,两者都不好......

通过@CommonsWare进行的更改非常完美,但不能直接应用于 API Level 8 Samples (下面的链接)的CameraPreview,所以这里是 diff :< / p>

以获得正确的纵横比:
方法是相同的,只需应用diff

public void setCamera(Camera camera, boolean portrait) {
    mCamera = camera;
    this.portrait = portrait;
    if (mCamera != null) {
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        requestLayout();
    }
}

public void switchCamera(Camera camera, boolean portrait) throws IOException {
   setCamera(camera, portrait);
   camera.setPreviewDisplay(mHolder);       
   Camera.Parameters parameters = camera.getParameters();
   parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
   requestLayout();

   camera.setParameters(parameters);
}

在:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {…}

变化:

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

to:

if (portrait && mPreviewSize != null) {
      previewWidth = mPreviewSize.height;
      previewHeight = mPreviewSize.width;
}
else if (mPreviewSize != null){
      previewWidth = mPreviewSize.width;
      previewHeight = mPreviewSize.height;
}

在:

private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {…}

变化:

double targetRatio = (double) w / h;

double targetRatio = 1;
if (portrait) {
    targetRatio= (double) w / h;
}

最终用肖像模式启动相机,例如:

try {
     mCamera = openFrontFacingCameraGingerbread();
     if (portraitMode) {
       mCamera.setDisplayOrientation(90);
     }
     preview.setCamera(mCamera, portraitMode);
} catch (Exception e) {
     e.printStackTrace();
     handleCameraUnavailable();
}

WUUHUUUU! 对我来说最终也没有预览问题,也没有保存图像的问题。 由于我的活动位置已被锁定,因此我通过OrientationChangedListener获取显示方向,以便以正确的方向保存图像。

使用类似的东西:
http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html

但它们已正确预览并保存。

我希望我能帮助很多人,有来自API Level 8的原始代码
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java 应用更改的地方。

答案 2 :(得分:-1)

请更改代码的相机方向,setDisplayorientation()getDisplayOrientation()

if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) 
{   
  camera.setDisplayOrientation(0);

}