You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Vision API设置预览尺寸无效,Android扫码界面拉伸求助

解决Google Vision API CameraSource预览拉伸问题

我来帮你彻底搞定这个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:56:03