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
相关产品推荐
相关产品推荐

