安卓相机预览错误的长宽比

我创build了一个基于教程的相机应用程序。 我使用的预览课程来自api-Demos“CameraPreview”。 我从这里添加了一个修改(预览总是旋转90°)。 所以这是我如何设置预览大小:

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); if (display.getRotation() == Surface.ROTATION_0) { parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width); mCamera.setDisplayOrientation(90); } if (display.getRotation() == Surface.ROTATION_90) { parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); } if (display.getRotation() == Surface.ROTATION_180) { parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width); } if (display.getRotation() == Surface.ROTATION_270) { parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setDisplayOrientation(180); } parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); requestLayout(); mCamera.setParameters(parameters); mCamera.startPreview(); } 

但“预览”显示的显示宽高比错误。 是因为上面的代码还是可能是因为我使用的布局?

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/button_capture" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="@string/capture" /> <FrameLayout android:id="@+id/camera_preview" android:layout_width="100dp" android:layout_height="match_parent"/> 

那么如何获得正确的宽高比? 提前致谢。

PS我读的答案来自: Android的相机预览看起来很奇怪但是这不适合我。

Solutions Collecting From Web of "安卓相机预览错误的长宽比"

尝试添加此function来更改预览大小:

 private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.05; double targetRatio = (double) w/h; if (sizes==null) return null; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Find size for (Camera.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); } } if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } 

并从这些优化值中设置尺寸:

 List<Camera.Size> sizes = parameters.getSupportedPreviewSizes(); Camera.Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); parameters.setPreviewSize(optimalSize.width, optimalSize.height); 

我希望这工作:)

最好的祝福

Henric

以下代码修改相机预览容器的宽度/高度,以匹配相机预览的宽高比。

  Camera.Size size = camera.getParameters().getPreviewSize(); //landscape float ratio = (float)size.width/size.height; //portrait //float ratio = (float)size.height/size.width; preview = (FrameLayout) findViewById(R.id.camera_preview); int new_width=0, new_height=0; if(preview.getWidth()/preview.getHeight()<ratio){ new_width = Math.round(preview.getHeight()*ratio); new_height = cameraPreview.getHeight(); }else{ new_width = preview.getWidth(); new_height = Math.round(preview.getWidth()/ratio); } preview.setLayoutParams(new FrameLayout.LayoutParams(new_width, new_height)); 

问题确实在于你布置的东西。 在Preview类中有覆盖onLayout 。 其工作的想法是根据find的最佳Size设置子SurfaceView Size 。 但是它不考虑轮换,所以你需要自己做:

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

代替

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

诀窍是交换宽度和高度,这是由于90度旋转完成的

  mCamera.setDisplayOrientation(90); 

您也可以考虑根据您设置的方向来设置子预览大小设置

  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { //... } 

(在我提供的代码中,它总是旋转90度,180度你不必做任何旋转,当你不设置旋转时,不需要调整宽度和高度)

另一件值得一提的事情 – 当计算getOptimalPreviewSize的情况下,当你有旋转,你交换子宽度和高度,你也应该通过父母( Preview )宽度和高度交换在onMeasure

 if (mSupportedPreviewSizes != null) { //noinspection SuspiciousNameCombination final int previewWidth = height; //noinspection SuspiciousNameCombination final int previewHeight = width; mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, previewWidth, previewHeight); } 

使用上面的解决scheme,使用私有的方法getOptimalPreviewSize(List sizes,int w,int h)。 工作很好! 纵向方向上的纵横比有问题:这是我的解决scheme。 与android的文档混合使用:

  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; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here Camera.Parameters params = mCamera.getParameters(); params.set("orientation", "portrait"); Size optimalSize=getOptimalPreviewSize(params.getSupportedPreviewSizes(), getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); params.setPreviewSize(optimalSize.width, optimalSize.height); mCamera.setParameters(params); // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } } 

Henric的答案对我不起作用,所以我创build了另一种方法来确定任何相机的最佳预览尺寸,给出目标视图的当前宽度和高度以及活动方向:

 public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) { if (CommonUtils.isEmpty(cameraPreviewSizes)) { return null; } int optimalHeight = Integer.MIN_VALUE; int optimalWidth = Integer.MIN_VALUE; for (Camera.Size cameraPreviewSize : cameraPreviewSizes) { boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width; int actualCameraWidth = cameraPreviewSize.width; int actualCameraHeight = cameraPreviewSize.height; if (isActivityPortrait) { if (!isCameraPreviewHeightBigger) { int temp = cameraPreviewSize.width; actualCameraWidth = cameraPreviewSize.height; actualCameraHeight = temp; } } else { if (isCameraPreviewHeightBigger) { int temp = cameraPreviewSize.width; actualCameraWidth = cameraPreviewSize.height; actualCameraHeight = temp; } } if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) { // finds only smaller preview sizes than target size continue; } if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) { // finds only better sizes optimalWidth = actualCameraWidth; optimalHeight = actualCameraHeight; } } Size optimalSize = null; if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) { optimalSize = new Size(optimalWidth, optimalHeight); } return optimalSize; } 

这使用了一个自定义的Size对象,因为Android的Size在API 21之后是可用的。

 public class Size { private int width; private int height; public Size(int width, int height) { this.width = width; this.height = height; } public int getHeight() { return height; } public int getWidth() { return width; } } 

您可以通过侦听其全局布局更改来确定视图的宽度和高度,然后可以设置新的维度。 这也显示了如何以编程方式确定活动方向:

 cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // gets called after layout has been done but before display. cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait); if (optimalCameraPreviewSize != null) { LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight()); cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams); } } });