我正在尝试让相机预览在纵向模式下正常工作,其中活动本身可以正常更改方向(即,不会锁定到横向)。
但setDisplayOrientation()
的使用严重打破了预览的行为。
谷歌自己的ApiDemos可以证明这一点。第一张图片基于CameraPreview
示例项目的android-17
版本中的ApiDemos
,其中我唯一的更改是从此活动的条目中移除android:orientation="landscape"
表现。下图是运行Android 4.2的Nexus 4显示屏上显示的内容的屏幕截图,相机指向8.5“方形纸张:
预览中不明显的是它旋转了90度。你在图像最右边看到的袜子包裹的脚实际上在广场下面。
至少在横向模式下,解决方法是使用setDisplayOrientation()
。但是,如果您将Preview
CameraPreview
的内部类修改为mCamera.setDisplayOrientation(90);
,则可以获得此结果:
值得注意的是,广场不再是正方形。
它使用与以前相同的SurfaceView
大小。我已使用ApiDemos
之外的其他代码重现了此方案,并且我在该代码中使用TextureView
获得了相同的行为。
我简单地想到问题可能是getSupportedPreviewSizes()
可能会根据setDisplayOrientation()
返回不同的值,但快速测试表明情况并非如此。
任何人都知道如何让setDisplayOrientation()
在纵向模式下工作而不会将图像的宽高比推到预览位置?
谢谢!
答案 0 :(得分:15)
好的,我想我想出来了。众所周知的谜题有几个部分,我感谢@kcoppock帮助我解决其中一些问题的见解。
首先,在确定要选择的预览尺寸时,需要考虑方向。例如,来自getOptimalPreviewSize()
的{{1}}的{{1}}无法定位,只是因为他们的应用版本的方向已锁定为横向。如果您希望方向浮动,则需要反转目标纵横比以匹配。那么,CameraPreview
有:
ApiDemos
你还需要:
getOptimalPreviewSize()
其中double targetRatio=(double)width / height;
是一个从0到360的值,我从大约100行的一些严重丑陋的代码中确定,这就是为什么我将所有这些包装在一个我将很快发布的可重用组件。该代码基于the setDisplayOrientation()
JavaDocs和this 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 * * *问题,过去两周我真的很疯狂。 好吧,很少有事情要知道:
通过@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);
}