相机setDisplayOrientation()在肖像模式下打破纵横比

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

但是, setDisplayOrientation()的使用会严重影响预览的行为。

这可以通过谷歌自己的ApiDemos来certificate。 第一个图像基于ApiDemos示例项目的android-17版本中的ApiDemos ,其中我所作的唯一更改是从清单中此活动的条目中移除android:orientation="landscape" 。 以下图片是运行Android 4.2的Nexus 4显示屏上显示的内容的截图,相机指向一个8.5平方英寸的纸张:

Nexus 4 ApiDemos预览,肖像模式

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

至less针对横向模式的解决scheme是使用setDisplayOrientation() 。 但是,如果您将CameraPreviewPreview内部类修改为具有mCamera.setDisplayOrientation(90); ,你得到这个:

带有setDisplayOrientation(90)的Nexus 4 ApiDemos预览,纵向模式

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

这使用与以前相同的SurfaceView尺寸。 我已经用ApiDemos之外的其他代码复制了这个场景,并且在该代码中使用TextureView获得了相同的行为。

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

任何人都知道如何让setDisplayOrientation()在纵向模式下工作,而不会破坏推送到预览的图像的纵横比?

谢谢!

好的,我想我明白了这一点。 有一些谚语拼图,我感谢@ kcoppock的洞察力,帮助我解决了这一些。

首先,在确定要select的预览尺寸时,需要考虑方向。 例如,来自CameraPreviewApiDemosgetOptimalPreviewSize()不理想,只是因为它们的应用程序版本的方向locking到横向。 如果您希望让方向浮动,您需要反转目标长宽比以匹配。 所以, getOptimalPreviewSize()有:

 double targetRatio=(double)width / height; 

你还需要:

 if (displayOrientation == 90 || displayOrientation == 270) { targetRatio=(double)height / width; } 

其中displayOrientation是一个从0到360的值,我从大约100行的一些严重丑陋的代码中确定,这就是为什么我把这些全部包装在一个我将很快发布的可重用组件中。 该代码是基于setDisplayOrientation() JavaDocs和这个StackOverflow的答案 。

(顺便说一句,如果你在2013年7月1日之后阅读这篇文章,而且你没有看到这个组件的答案的链接,请发表评论提醒我,因为我忘了)

其次,在控制所使用的SurfaceView / TextureView的纵横比时,需要考虑显示方向。 CameraPreview活动具有自己的Preview ViewGroup ,它可以处理纵横比,并且您需要反转纵横比以供纵向使用:

  if (displayOrientation == 90 || displayOrientation == 270) { previewWidth=mPreviewSize.height; previewHeight=mPreviewSize.width; } else { previewWidth=mPreviewSize.width; previewHeight=mPreviewSize.height; } 

其中displayOrientation是相同的值( 90270是纵向和反向纵向,注意我还没有尝试获得反向纵向或反向横向工作,所以可能需要更多的调整)。

第三 – 我发现真气 – 你必须在setPictureSize()上调用setPictureSize()之前开始预览。 否则,就好像图片的宽高比被应用到预览帧,搞砸了。

所以我曾经有类似于以下的代码:

 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); 

没有这个变化,即使在风景中,我也会得到一个拉伸的高宽比。 这对CameraPreviewApiDemos不是问题,因为它们没有拍照,所以它们从来没有调用过setPictureSize()

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

如果遇到其他设备需要的其他黑客手段,以及在发布时链接到我的CameraView / CameraFragment组件,我会尝试记住修改此问题。


更新#1

那么,当然,这可能几乎没有这么简单。 🙂

解决问题的地方setPictureSize()拧紧高宽比工程…直到你拍照。 此时,预览切换到错误的宽高比。

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

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

  • 仅在拍摄照片之前更新Camera.Parameters的图片尺寸等
  • Camera.Parameters后恢复到拍照前的Camera.Parameters

在照片拍摄期间,这仍然呈现错误的宽高比。 这个长期的解决scheme – 我认为 – 将是在拍照的时候用静止图像(最后一个预览帧)暂时replace相机预览。 我会最终尝试这个。


更新#2 : 我的CWAC相机库的 v0.0.1现在可用,包含上述代码。

好吧,我真的在过去的两周里对这个f * * *问题发疯。 好吧,有几件事要知道:

  • 有时你只是得到黑色边框 ,如果操作栏或软button是可见的,只是可能在某些设备上, 我的build议: * 不关心 *,否则你会发疯,或削减预览,都不好。

@CommonsWare的改变是完美的,但不能直接应用于API Level 8 Samples的CameraPreview(下面的链接),所以这里是diff

正确的长宽比在肖像做:
方法是一样的,只是应用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; } 

至:

 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! 对于我来说终于也没有预览的问题,并没有保存图像的问题。 由于我的活动位置已locking,因此我通过OrientationChangedListener获取了显示方向,以正确的方向保存图像。

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

但他们正确预览和保存。

我希望我可以帮助很多人,有API级别8原始代码
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java进行更改&#x3002;

请改变你的代码相机的方向,你应该设置setDisplayorientation()getDisplayOrientation()

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