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

Android中SurfaceView二维码阅读器的自动对焦实现求助

解决SurfaceView二维码阅读器的自动对焦问题

嘿,我明白你现在的困扰——用SurfaceView做二维码阅读器,却卡在自动对焦上了对吧?别担心,我来帮你搞定这个问题!

首先咱们得搞清楚核心原因:你用的是Google Vision库的CameraSource,这个类默认并没有开启自动对焦功能,所以需要咱们手动去设置底层Camera的参数。因为CameraSource没有直接暴露Camera实例,咱们得用反射来获取它,然后修改对焦模式。

具体实现步骤:

  1. 添加自动对焦设置方法
    在你的page_qr类里新增一个专门设置自动对焦的方法,通过反射获取Camera实例并调整参数:
private void setAutoFocus(CameraSource cameraSource) {
    try {
        // 通过反射获取CameraSource里的私有mCamera字段
        Field cameraField = CameraSource.class.getDeclaredField("mCamera");
        cameraField.setAccessible(true);
        Camera camera = (Camera) cameraField.get(cameraSource);
        
        if (camera != null) {
            Camera.Parameters params = camera.getParameters();
            
            // 优先选择连续自动对焦(适合扫码场景)
            List<String> supportedFocusModes = params.getSupportedFocusModes();
            if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            } 
            // 设备不支持连续拍照对焦的话,用连续视频对焦
            else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            } 
            // 以上都不支持的话, fallback到单次自动对焦
            else {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            }
            
            camera.setParameters(params);
        }
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}
  1. 启动相机后调用自动对焦方法
    在surfaceCreated方法里,启动cameraSource之后立即调用上面的方法,确保相机启动后马上开启自动对焦:
@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        cameraSource.start(cameraView.getHolder());
        // 启动相机后立刻设置自动对焦
        setAutoFocus(cameraSource);
    } catch (IOException ie) {
        Log.e("CAMERA SOURCE", ie.getMessage());
    }
}
  1. 补上相机权限处理(重要)
    如果你的App targetSdkVersion >=23(Android 6.0+),必须动态申请相机权限,否则相机无法正常启动。咱们把权限检查和相机初始化拆分开,让代码更清晰:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.page_qr);
    _C = this;
    cameraView = (SurfaceView) findViewById(R.id.camera_view);
    barcodeInfo = (TextView) findViewById(R.id.code_info);
    
    // 先检查相机权限
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 100);
    } else {
        // 权限已允许,初始化相机相关逻辑
        initCamera();
    }
}

// 相机初始化逻辑抽成单独方法,方便复用
private void initCamera() {
    barcodeDetector = new BarcodeDetector.Builder(this)
            .setBarcodeFormats(Barcode.QR_CODE)
            .build();
    cameraSource = new CameraSource
            .Builder(this, barcodeDetector)
            .setRequestedPreviewSize(640, 480)
            .setFacing(CameraSource.CAMERA_FACING_BACK)
            .setRequestedFps(30.0f)
            .build();
    
    cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                cameraSource.start(cameraView.getHolder());
                setAutoFocus(cameraSource);
            } catch (IOException ie) {
                Log.e("CAMERA SOURCE", ie.getMessage());
            }
        }
        
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            cameraSource.stop();
        }
    });
    
    barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
        @Override
        public void release() {}
        
        @Override
        public void receiveDetections(Detector.Detections<Barcode> detections) {
            final SparseArray<Barcode> barcodes = detections.getDetectedItems();
            if (barcodes.size() != 0) {
                barcodeInfo.post(() -> barcodeInfo.setText(barcodes.valueAt(0).displayValue));
            }
        }
    });
}

// 权限申请回调处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 100) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initCamera();
        } else {
            Toast.makeText(this, "需要相机权限才能扫描二维码哦", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

补充说明:

  • 你布局里给SurfaceView设置的focusable="true"和focusableInTouchMode="true"是没问题的,但这和相机自动对焦无关,主要是确保SurfaceView能获取焦点,不影响咱们的对焦设置。
  • 反射的方式是因为CameraSource的mCamera字段是私有的,目前这个方法在大多数设备上都能正常工作,如果后续Google Vision库更新字段名,可能需要微调,但现阶段是最直接的解决方案。

这样修改之后,你的二维码阅读器应该就能自动对焦了,试试看吧!

内容的提问来源于stack exchange,提问作者Naser Ghafari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:21:35