Google Vision API设置预览尺寸无效,Android扫码界面拉伸求助
我来帮你彻底搞定这个SurfaceView预览拉伸的问题!你遇到的核心问题其实有两个:一是setRequestedPreviewSize()不是随便传个数值就能生效的,二是SurfaceView的布局宽高比必须和相机实际使用的预览尺寸宽高比一致,否则必然会出现拉伸失真。
问题根源分析
Google Vision的CameraSource只会从设备相机支持的预览尺寸列表里选择最接近你设置的尺寸,如果你传入的屏幕尺寸不在这个列表里,设置就会被忽略——这就是为什么你改数值没效果的原因。另外,相机传感器本身是横屏的,所以预览尺寸的宽高比通常是16:9、4:3这类,直接用屏幕的宽高(尤其是竖屏设备)会导致比例不匹配,进而拉伸。
完整解决方案
我们需要分两步走:先找到相机支持的、和屏幕比例最匹配的预览尺寸,再让SurfaceView严格遵循这个比例布局。
1. 获取相机支持的预览尺寸列表并匹配最佳比例
首先,我们需要通过CameraSource获取它支持的所有预览尺寸,然后筛选出和屏幕宽高比最接近的那个。注意要处理竖屏的情况,因为相机预览的尺寸是按横屏定义的,我们需要转换比例。
添加一个工具方法来获取最佳预览尺寸:
private Size getOptimalPreviewSize(CameraSource cameraSource, int screenWidth, int screenHeight) { List<Size> supportedSizes = cameraSource.getPreviewSizes(); if (supportedSizes == null || supportedSizes.isEmpty()) { return new Size(screenWidth, screenHeight); } float screenRatio = (float) screenWidth / screenHeight; // 因为相机是横屏的,所以需要考虑两种比例:原比例和翻转后的比例 float targetRatio = Math.max(screenRatio, 1/screenRatio); Size optimalSize = null; float minDiff = Float.MAX_VALUE; for (Size size : supportedSizes) { float previewRatio = (float) size.getWidth() / size.getHeight(); // 同样处理预览尺寸的比例,和目标比例对比 float currentRatio = Math.max(previewRatio, 1/previewRatio); float diff = Math.abs(currentRatio - targetRatio); if (diff < minDiff) { minDiff = diff; optimalSize = size; } else if (diff == minDiff) { // 如果比例相同,选择分辨率更高的 int currentArea = size.getWidth() * size.getHeight(); int optimalArea = optimalSize.getWidth() * optimalSize.getHeight(); if (currentArea > optimalArea) { optimalSize = size; } } } return optimalSize != null ? optimalSize : supportedSizes.get(0); }
2. 初始化CameraSource并设置最佳尺寸
在你的onCreate方法里,替换原来的setRequestedPreviewSize代码:
// 获取屏幕尺寸 DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; barcodeDetector = new BarcodeDetector.Builder(this) .setBarcodeFormats(Barcode.QR_CODE) .build(); cameraSource = new CameraSource.Builder(this, barcodeDetector) .setAutoFocusEnabled(true) .build(); // 获取最佳预览尺寸 Size optimalSize = getOptimalPreviewSize(cameraSource, screenWidth, screenHeight); // 设置预览尺寸(注意相机是横屏,所以如果是竖屏设备,这里的宽高是传感器的宽高,不需要翻转) cameraSource.setRequestedPreviewSize(optimalSize.getWidth(), optimalSize.getHeight());
3. 调整SurfaceView的布局比例
接下来,让SurfaceView的宽高比和最佳预览尺寸的比例完全一致,这样就不会拉伸了。在surfaceCreated或者onCreate里设置LayoutParams:
// 计算预览尺寸的宽高比(注意竖屏时要翻转比例) float previewRatio; if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { // 竖屏时,预览尺寸的宽高是横屏的,所以比例是 height/width previewRatio = (float) optimalSize.getHeight() / optimalSize.getWidth(); } else { previewRatio = (float) optimalSize.getWidth() / optimalSize.getHeight(); } // 设置SurfaceView的LayoutParams,保持比例 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ); params.height = (int) (screenWidth * previewRatio); svCamera.setLayoutParams(params);
为什么你的临时方案有效?
你硬编码的1280x720是标准的16:9比例,而你设置SurfaceView的尺寸为width, width/9*16也是16:9比例,所以比例匹配就不会拉伸。上面的方案就是把这个逻辑通用化,自动适配所有设备的支持尺寸。
额外注意事项
- 别忘了处理权限请求,确保相机权限已经授予
- 在
surfaceChanged方法里,如果屏幕方向变化,最好重新计算并设置SurfaceView的布局比例 - 有些设备可能支持的预览尺寸不多,这个方法会自动选最接近的,保证效果最优
内容的提问来源于stack exchange,提问作者Jeroen Ritmeester

