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

Android相机预览尺寸问题:二维码扫描预览代码无法运行求助

解决Android相机预览尺寸与二维码扫描的运行问题

看起来你在做二维码扫描的相机预览时遇到了卡壳,从你给出的代码片段来看,核心问题大概率出在预览尺寸不匹配以及相机初始化流程不完整上,我来一步步帮你排查修复:

1. 先把最基础的权限问题搞定

Android 6.0+必须动态申请相机权限,没权限的话相机实例根本拿不到,直接就崩了。先加这段权限检查代码:

private static final int CAMERA_PERMISSION_REQUEST = 1001;

// 在onCreate开头或者启动相机前调用
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST);
    return;
}

别忘了在AndroidManifest.xml里也加上静态权限声明:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

2. 解决预览尺寸不匹配的核心问题

相机支持的预览尺寸是有限的,必须选一个和你的预览容器(SurfaceView/TextureView)比例一致的尺寸,不然要么拉伸变形,要么直接黑屏启动失败。我给你写个获取最优预览尺寸的工具方法:

private Camera.Size getOptimalPreviewSize(List<Camera.Size> supportedSizes, int targetWidth, int targetHeight) {
    final double ASPECT_TOLERANCE = 0.1; // 比例误差容忍度
    double targetRatio = (double) targetWidth / targetHeight;

    Camera.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    // 优先找比例匹配的尺寸
    for (Camera.Size size : supportedSizes) {
        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 : supportedSizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }
    return optimalSize;
}

然后在设置相机参数的时候用上这个方法:

// 获取你之前拿到的Display尺寸
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);

// 配置相机参数
Camera.Parameters params = mCamera.getParameters();
List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
Camera.Size optimalSize = getOptimalPreviewSize(previewSizes, size.x, size.y);
params.setPreviewSize(optimalSize.width, optimalSize.height);
// 别忘了开启自动对焦,二维码扫描需要这个
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setParameters(params);

3. 完善相机预览的绑定流程

你的代码里只拿到了相机实例和Display尺寸,但没把相机和预览容器绑定起来。必须实现SurfaceHolder.Callback来处理Surface的生命周期:

private SurfaceHolder mPreviewHolder;

// 在onCreate里初始化SurfaceView
FrameLayout previewLayout = findViewById(R.id.camera_preview);
SurfaceView surfaceView = new SurfaceView(this);
mPreviewHolder = surfaceView.getHolder();
mPreviewHolder.addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // Surface创建后绑定相机预览
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.w("CameraError", "绑定预览失败: " + e.getMessage());
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // 尺寸变化时重新调整预览
        if (mPreviewHolder.getSurface() == null) return;

        try {
            mCamera.stopPreview();
            // 重新设置最优尺寸
            Camera.Parameters params = mCamera.getParameters();
            Camera.Size optimalSize = getOptimalPreviewSize(params.getSupportedPreviewSizes(), width, height);
            params.setPreviewSize(optimalSize.width, optimalSize.height);
            mCamera.setParameters(params);
            mCamera.setPreviewDisplay(mPreviewHolder);
            mCamera.startPreview();
        } catch (Exception e) {
            Log.w("CameraError", "重启预览失败: " + e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // 页面销毁时释放相机资源,避免内存泄漏
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
});
previewLayout.addView(surfaceView);

4. 修复你的Controllo实例初始化逻辑

你现在的代码里直接抛出RuntimeException,会导致程序直接崩溃,改成更友好的处理方式:

try {
    c = ((Class<? extends Controllo>) getIntent().getExtras().get("ctrl")).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
    Log.w("InitError", "Controllo初始化失败: " + e.toString());
    Toast.makeText(this, "初始化失败,请重试", Toast.LENGTH_SHORT).show();
    finish(); // 直接关闭当前页面,避免后续逻辑出错
    return;
}

最后补充点小细节

  • 测试时尽量用真机,模拟器的相机功能经常有问题
  • 如果是Android 5.0以上的设备,也可以考虑用TextureView替代SurfaceView,预览效果更稳定
  • 二维码扫描的解码逻辑要放在相机预览的回调里,别阻塞主线程

按照这些步骤调整后,你的相机预览应该就能正常运行了。

内容的提问来源于stack exchange,提问作者Alessandro Zago

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:32:49