Android相机预览拉伸

我一直在努力在Android上制作自定义相机活动,但是在旋转相机时,表面视图的纵横比会变差。

在我的活动中,我设置了用于保存显示相机参数的表面视图的framelayout。

//FrameLayout that will hold the camera preview FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview); //Setting camera's preview size to the best preview size Size optimalSize = null; camera = getCameraInstance(); double aspectRatio = 0; if(camera != null){ //Setting the camera's aspect ratio Camera.Parameters parameters = camera.getParameters(); List<Size> sizes = parameters.getSupportedPreviewSizes(); optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); aspectRatio = (float)optimalSize.width/optimalSize.height; } if(optimalSize!= null){ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio)); previewHolder.setLayoutParams(params); LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio)); cameraPreview.setLayoutParams(surfaceParams); } cameraPreview.setCamera(camera); //Adding the preview to the holder previewHolder.addView(cameraPreview); 

然后,在曲面视图中,我设置了要显示的相机参数

 public void setCamera(Camera camera) { if (mCamera == camera) { return; } mCamera = camera; if (mCamera != null) { requestLayout(); try { mCamera.setPreviewDisplay(mHolder); } catch (IOException e) { e.printStackTrace(); } if(mCamera != null){ //Setting the camera's aspect ratio Camera.Parameters parameters = mCamera.getParameters(); List<Size> sizes = parameters.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); parameters.setPreviewSize(optimalSize.width, optimalSize.height); mCamera.setParameters(parameters); } /* Important: Call startPreview() to start updating the preview surface. Preview must be started before you can take a picture. */ mCamera.startPreview(); } } 

在这里输入图像说明

你可以看到,当电话旋转时,乐高的男人变得更高,更瘦。

我怎样才能确保我的相机视图的高宽比是正确的?

我正在使用这种方法 – >基于API演示得到我的预览大小:

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

正如你所看到的,你必须input你的屏幕的宽度和高度。 此方法将根据这些值计算屏幕比例,然后从supportedPreviewSizes列表中select最适合您的可用框架。 通过使用Camera对象不为null的位置获取您的supportedPreviewSize列表

 mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 

然后在onMeasure,你可以得到你的最佳previewSize像这样:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); if (mSupportedPreviewSizes != null) { mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); } } 

然后(在我的代码中的surfaceChanged方法,就像我说的我使用CameraActivity代码的API演示结构,您可以在Eclipse中生成它):

 Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setParameters(parameters); mCamera.startPreview(); 

还有一个提示,因为我做了几乎像你一样的应用程序。 Camera Activity的最佳做法是隐藏StatusBar。 像Instagram的应用程序正在这样做。 它会降低屏幕高度值并更改比率值。 在某些设备上可能会出现奇怪的预览尺寸(您的SurfaceView将被剪切)


并回答你的问题,如何检查你的预览比例是否正确? 然后获取您设置的参数的高度和宽度:

 mCamera.setParameters(parameters); 

你的设定比例等于高度/宽度。 如果您希望相机在屏幕上看起来不错,则设置为相机的参数的高度/宽度比率必须与屏幕的高度(减去状态栏)/宽度比率相同。

F1Sher的解决scheme很好,但有时不起作用。 特别是,当你的surfaceView不覆盖整个屏幕。 在这种情况下,您需要重写onMeasure()方法。 我已经在这里复制我的代码供您参考。

因为我测量的是基于宽度的surfaceView,所以我在屏幕的最后有一点白色的空白,我用devise填充了它。 如果您保持高度并将宽度乘以比率,则可以解决此问题。 但是,它会稍微挤压surfaceView。

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "CameraPreview"; private Context mContext; private SurfaceHolder mHolder; private Camera mCamera; private List<Camera.Size> mSupportedPreviewSizes; private Camera.Size mPreviewSize; public CameraPreview(Context context, Camera camera) { super(context); mContext = context; mCamera = camera; // supported preview sizes mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); for(Camera.Size str: mSupportedPreviewSizes) Log.e(TAG, str.width + "/" + str.height); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // empty. surfaceChanged will take care of stuff } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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 // start preview with new settings try { Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setParameters(parameters); mCamera.setDisplayOrientation(90); mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); if (mSupportedPreviewSizes != null) { mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); } if (mPreviewSize!=null) { float ratio; if(mPreviewSize.height >= mPreviewSize.width) ratio = (float) mPreviewSize.height / (float) mPreviewSize.width; else ratio = (float) mPreviewSize.width / (float) mPreviewSize.height; // One of these methods should be used, second method squishes preview slightly setMeasuredDimension(width, (int) (width * ratio)); // setMeasuredDimension((int) (width * ratio), height); } } private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) h / w; if (sizes == null) return null; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; for (Camera.Size size : sizes) { double ratio = (double) size.height / size.width; 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; } } 

注意:我的解决scheme是HESAM解决scheme的延续: https ://stackoverflow.com/a/22758359/1718734

我说的是:Hesam说有些手机上可能会出现一个小小的空白区域,像这样:

注意:虽然宽高比是正确的,但相机不会填满整个屏幕。

Hesam提出了第二个解决scheme,但是这样会缩小预览范围。 而在某些设备上,它会严重扭曲。

那么我们如何解决这个问题。 这是简单的…通过乘以长宽比,直到它填充屏幕。 我注意到,一些stream行的应用程序,如Snapchat,WhatsApp等工作方式相同。

你所要做的就是把这个添加到onMeasure方法中:

 float camHeight = (int) (width * ratio); float newCamHeight; float newHeightRatio; if (camHeight < height) { newHeightRatio = (float) height / (float) mPreviewSize.height; newCamHeight = (newHeightRatio * camHeight); Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight); setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight); Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight); } else { newCamHeight = camHeight; setMeasuredDimension(width, (int) newCamHeight); Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight); } 

这将计算屏幕高度,并获得屏幕高度和mPreviewSize高度的比率。 然后,将相机的宽度和高度乘以新的高度比率,并相应地设置测量的尺寸。

在这里输入图像说明

接下来你知道,你最终得到这个:D

在这里输入图像说明

这也适用于他的前置摄像头。 我相信这是最好的办法。 现在唯一留给我的应用程序是保存预览本身点击“捕获”。 但是,这是它。

好的,所以我觉得一般的相机预览拉伸问题没有足够的答案 。 或者至less我没有find一个。 我的应用程序也遭受了这种拉伸综合征,我花了一段时间来拼凑在这个门户网站和互联网上的所有用户答案的​​解决scheme。

我试过@ Hesa​​m的解决scheme,但它不起作用,使我的相机预览大大扭曲。

首先显示我的解决scheme的代码(代码的重要部分),然后解释为什么我采取了这些步骤。 有性能修改的空间。

主要活动xml布局:

 <RelativeLayout android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <FrameLayout android:id="@+id/camera_preview" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> 

相机预览:

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder prHolder; private Camera prCamera; public List<Camera.Size> prSupportedPreviewSizes; private Camera.Size prPreviewSize; @SuppressWarnings("deprecation") public YoCameraPreview(Context context, Camera camera) { super(context); prCamera = camera; prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes(); prHolder = getHolder(); prHolder.addCallback(this); prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { try { prCamera.setPreviewDisplay(holder); prCamera.startPreview(); } catch (IOException e) { Log.d("Yologram", "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (prHolder.getSurface() == null){ return; } try { prCamera.stopPreview(); } catch (Exception e){ } try { Camera.Parameters parameters = prCamera.getParameters(); List<String> focusModes = parameters.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height); prCamera.setParameters(parameters); prCamera.setPreviewDisplay(prHolder); prCamera.startPreview(); } catch (Exception e){ Log.d("Yologram", "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); if (prSupportedPreviewSizes != null) { prPreviewSize = getOptimalPreviewSize(prSupportedPreviewSizes, width, height); } } public Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) h / w; if (sizes == null) return null; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; 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; } } 

主要活动:

 public class MainActivity extends Activity { ... @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); maCamera = getCameraInstance(); maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview); maPreview = new CameraPreview(this, maCamera); Point displayDim = getDisplayWH(); Point layoutPreviewDim = calcCamPrevDimensions(displayDim, maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes, displayDim.x, displayDim.y)); if (layoutPreviewDim != null) { RelativeLayout.LayoutParams layoutPreviewParams = (RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams(); layoutPreviewParams.width = layoutPreviewDim.x; layoutPreviewParams.height = layoutPreviewDim.y; layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT); maLayoutPreview.setLayoutParams(layoutPreviewParams); } maLayoutPreview.addView(maPreview); } @SuppressLint("NewApi") @SuppressWarnings("deprecation") private Point getDisplayWH() { Display display = this.getWindowManager().getDefaultDisplay(); Point displayWH = new Point(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { display.getSize(displayWH); return displayWH; } displayWH.set(display.getWidth(), display.getHeight()); return displayWH; } private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) { Point displayDim = disDim; Camera.Size cameraDim = camDim; double widthRatio = (double) displayDim.x / cameraDim.width; double heightRatio = (double) displayDim.y / cameraDim.height; // use ">" to zoom preview full screen if (widthRatio < heightRatio) { Point calcDimensions = new Point(); calcDimensions.x = displayDim.x; calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width; return calcDimensions; } // use "<" to zoom preview full screen if (widthRatio > heightRatio) { Point calcDimensions = new Point(); calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height; calcDimensions.y = displayDim.y; return calcDimensions; } return null; } } 

我的评论:

所有这一切的一点是,尽pipe你在getOptimalPreviewSize()计算出最佳的相机尺寸 ,但你只能select适合你的屏幕的比例 。 所以除非比例完全相同否则预览会延伸。

为什么会延伸? 因为您的FrameLayout相机预览在layout.xml中设置为match_parent的宽度和高度。 所以这就是为什么预览会延伸到全屏。

需要做的事情就是设置相机预览的布局宽度和高度,以匹配所选的相机尺寸比例 ,所以预览保持其纵横比不会变形。

我试图使用CameraPreview类来做所有的计算和布局的变化,但我无法弄清楚。 我试图应用此解决scheme ,但SurfaceView不识别getChildCount ()getChildAt (int index) 。 我想,我最终参考了maLayoutPreview ,但它是行为不当,并将设置比例应用到我的整个应用程序,并在第一张照片拍摄后,这样做。 所以我放手,将布局修改移到MainActivity

CameraPreview我将prSupportedPreviewSizesgetOptimalPreviewSize()更改为public,所以我可以在MainActivity使用它。 然后,我需要显示尺寸 (如果有的话,减去导航/状态栏),并select最佳的相机尺寸 。 我试图得到RelativeLayout(或FrameLayout)大小,而不是显示大小,但它返回零值。 这个解决scheme对我不起作用。 在onWindowFocusChanged (在日志中检查)之后,布局得到了它的值。

所以我有我的方法来计算布局尺寸以匹配所选相机尺寸的长宽比。 现在你只需要设置你的相机预览布局的LayoutParams 。 更改宽度,高度并将其居中。

如何计算预览尺寸有两个select。 要么让它适合屏幕上的黑色条 (如果windowBackground设置为null)的侧面或顶部/底部。 或者你想预览放大到全屏 。 我在calcCamPrevDimensions()留下了更多信息的评论。

嗨在这里getOptimalPreview()没有为我工作,所以我想分享我的版本:

 private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { if (sizes==null) return null; Camera.Size optimalSize = null; double ratio = (double)h/w; double minDiff = Double.MAX_VALUE; double newDiff; for (Camera.Size size : sizes) { newDiff = Math.abs((double)size.width/size.height - ratio); if (newDiff < minDiff) { optimalSize = size; minDiff = newDiff; } } return optimalSize; } 

只是为了使这个线程更完整我添加我的版本的答案:

我想达到的目的:表面视图不应该被拉伸,它应该覆盖整个屏幕,而且,我的应用程序中只有一个景观模式。

解:

该解决scheme是F1sher解决scheme的极小扩展:

=>第一步是整合F1sher的解决scheme。

=>现在,当表面视图不覆盖整个屏幕时,F1sher的解决scheme中可能会出现一个场景。解决scheme是使表面视图大于屏幕尺寸,以覆盖整个屏幕,因为:

  size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight); Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(size.width, size.height); mCamera.setParameters(parameters); double screenRatio = (double) screenHeight / screenWidth; double previewRatio = (double) size.height / size.width; if (previewRatio > screenRatio) /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/ { RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio)); params1.addRule(RelativeLayout.CENTER_IN_PARENT); flPreview.setLayoutParams(params1); flPreview.setClipChildren(false); LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio)); surfaceParams.gravity = Gravity.CENTER; mPreview.setLayoutParams(surfaceParams); } else /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/ { RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight); params1.addRule(RelativeLayout.CENTER_IN_PARENT); flPreview.setLayoutParams(params1); flPreview.setClipChildren(false); LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight); surfaceParams.gravity = Gravity.CENTER; mPreview.setLayoutParams(surfaceParams); } flPreview.addView(mPreview); /* The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView */ 

您必须根据所需的宽高比设置cameraView.getLayoutParams()。height和cameraView.getLayoutParams()。width。

我找出了什么问题 – 这是方向的变化。 如果将相机方向更改为90或270度,则需要更换支持的尺寸的宽度和高度 ,并且全部都可以。

表面视图应该位于框架布局中并具有中心重力。

这里是C#(Xamarin)的例子:

 public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height) { _camera.StopPreview(); // find best supported preview size var parameters = _camera.GetParameters(); var supportedSizes = parameters.SupportedPreviewSizes; var bestPreviewSize = supportedSizes .Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees .OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2)) .First(); if (height == bestPreviewSize.Height && width == bestPreviewSize.Width) { // start preview if best supported preview size equals current surface view size parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height); _camera.SetParameters(parameters); _camera.StartPreview(); } else { // if not than change surface view size to best supported (SurfaceChanged will be called once again) var layoutParameters = _surfaceView.LayoutParameters; layoutParameters.Width = bestPreviewSize.Width; layoutParameters.Height = bestPreviewSize.Height; _surfaceView.LayoutParameters = layoutParameters; } } 

请注意,相机参数应设置为原始尺寸(未交换),并且表面视图尺寸应交换。

我尝试了上面所有的解决scheme,但没有一个适合我。 最后,我自己解决了这个问题,并且发现其实很简单。 有两点你需要小心。

 parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); 

此预览大小必须是相机支持的分辨率之一,可以通过以下方式获取:

 List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes(); 

通常其中一个rawSupportedSize等于设备分辨率。

其次,将您的SurfaceView放在FrameLayout中,并像上面那样在surfaceChanged方法中设置曲面布局的高度和宽度

 FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams(); layoutParams.height = cameraResolution.x; layoutParams.width = cameraResolution.y; 

好的,事情做完了,希望这可以帮助你。

我的要求是相机预览需要全屏并保持宽高比。 Hesam和Yoosuf的解决scheme非常棒,但由于某些原因,我确实看到了高倍率的问题。

这个想法是一样的,在父级中有预览容器中心,并根据长宽比增加宽度或高度,直到它可以覆盖整个屏幕。

有一件事要注意的是预览尺寸是在横向,因为我们设置显示方向。

camera.setDisplayOrientation(90);

容器,我们将添加SurfaceView视图:

 <RelativeLayout android:id="@+id/camera_preview_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true"/> 

在您的活动中将预览添加到父级中心的容器中。

 this.cameraPreview = new CameraPreview(this, camera); cameraPreviewContainer.removeAllViews(); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); cameraPreviewContainer.addView(cameraPreview, 0, params); 

在CameraPreview类中:

 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 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 (holder.getSurface() == null) { // preview surface does not exist return; } stopPreview(); // set preview size and make any resize, rotate or // reformatting changes here try { Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera); Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(optimalSize.width, optimalSize.height); parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height); camera.setParameters(parameters); camera.setDisplayOrientation(90); camera.setPreviewDisplay(holder); camera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); if (supportedPreviewSizes != null && optimalSize == null) { optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height); Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h"); } float previewRatio = (float) optimalSize.height / (float) optimalSize.width; // previewRatio is height/width because camera preview size are in landscape. float measuredSizeRatio = (float) width / (float) height; if (previewRatio >= measuredSizeRatio) { measuredHeight = height; measuredWidth = (int) ((float)height * previewRatio); } else { measuredWidth = width; measuredHeight = (int) ((float)width / previewRatio); } Log.i(TAG, "Preview size: " + width + "w, " + height + "h"); Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h"); setMeasuredDimension(measuredWidth, measuredHeight); } 

我放弃了计算,并简单地获取了我希望显示相机预览的视图的大小,并在我的自定义SurfaceView实现中将相机的预览大小设置为相同(由于旋转只是将宽度/高度翻转过来):

 @Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Display display = ((WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); if (display.getRotation() == Surface.ROTATION_0) { final Camera.Parameters params = camera.getParameters(); // viewParams is from the view where the preview is displayed params.setPreviewSize(viewParams.height, viewParams.width); camera.setDisplayOrientation(90); requestLayout(); camera.setParameters(params); } // I do not enable rotation, so this can otherwise stay as is }