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

求助:如何在Xamarin Forms中实现Google Mobile Vision条码扫描?

在Xamarin.Forms中集成Google Mobile Vision实现条码扫描

嘿,我完全懂你的处境——Zxing对付常规条码没问题,但碰到小型条码就有点力不从心了。别担心,我来一步步带你搞定在Xamarin.Forms里集成Google Mobile Vision的流程,这个框架对小条码的识别精度确实更高,而且完美支持Android和iOS双平台~

先做准备:安装必要的NuGet包

  • Android项目:安装Xamarin.GooglePlayServices.Vision,这是Google Mobile Vision在Android端的官方绑定包
  • iOS项目:安装Xamarin.Google.MobileVision.BarCode,对应iOS端的条码扫描组件
  • Forms共享项目:不需要额外安装包,我们会通过依赖服务来调用原生实现

步骤1:在Forms层定义共享接口

因为Xamarin.Forms是跨平台的,我们需要先定义一个统一的接口,让Android和iOS各自实现扫描逻辑。在你的Forms项目里新建一个接口IBarcodeScanner:

public interface IBarcodeScanner
{
    // 异步方法:启动扫描并返回条码内容,取消或失败返回null
    Task<string> ScanBarcodeAsync();
}

步骤2:Android端实现扫描功能

2.1 配置相机权限

打开Android项目的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.2 编写扫描逻辑实现类

在Android项目里新建BarcodeScannerImplementation类,实现我们定义的IBarcodeScanner接口,同时处理权限申请、相机预览和条码检测:

[assembly: Dependency(typeof(BarcodeScannerImplementation))]
namespace YourAppName.Droid
{
    public class BarcodeScannerImplementation : IBarcodeScanner, IBarcodeCallback
    {
        private TaskCompletionSource<string> _taskCompletionSource;
        private BarcodeDetector _barcodeDetector;
        private CameraSource _cameraSource;
        private SurfaceView _surfaceView;
        private AlertDialog _dialog;

        public async Task<string> ScanBarcodeAsync()
        {
            _taskCompletionSource = new TaskCompletionSource<string>();

            // 先检查相机权限(Android 6.0+需要动态申请)
            var cameraPermissionStatus = ContextCompat.CheckSelfPermission(Android.App.Application.Context, Manifest.Permission.Camera);
            if (cameraPermissionStatus != Permission.Granted)
            {
                var currentActivity = (Activity)Android.App.Application.Context;
                ActivityCompat.RequestPermissions(currentActivity, new[] { Manifest.Permission.Camera }, 100);
                
                // 简单等待用户授权(实际项目可以优化为监听权限回调)
                await Task.Delay(1000);
                if (ContextCompat.CheckSelfPermission(Android.App.Application.Context, Manifest.Permission.Camera) != Permission.Granted)
                {
                    _taskCompletionSource.SetResult(null);
                    return await _taskCompletionSource.Task;
                }
            }

            // 创建条码检测器,支持所有条码格式
            _barcodeDetector = new BarcodeDetector.Builder(Android.App.Application.Context)
                .SetBarcodeFormats(BarcodeFormat.AllFormats)
                .Build();

            if (!_barcodeDetector.IsOperational)
            {
                // 检测器初始化失败(可能缺少模型文件,确保设备能访问网络)
                _taskCompletionSource.SetResult(null);
                return await _taskCompletionSource.Task;
            }

            // 创建相机源,开启自动对焦,设置预览分辨率
            _cameraSource = new CameraSource.Builder(Android.App.Application.Context, _barcodeDetector)
                .SetAutoFocusEnabled(true)
                .SetRequestedPreviewSize(1920, 1080)
                .Build();

            // 创建预览视图
            _surfaceView = new SurfaceView(Android.App.Application.Context);
            _cameraSource.Start(_surfaceView.Holder);

            // 绑定条码检测回调
            _barcodeDetector.SetProcessor(new BarcodeProcessor(this));

            // 显示扫描对话框
            var builder = new AlertDialog.Builder(Android.App.Application.Context);
            builder.SetView(_surfaceView);
            builder.SetNegativeButton("取消", (s, e) =>
            {
                CleanupResources();
                _taskCompletionSource.SetResult(null);
            });
            _dialog = builder.Create();
            _dialog.Show();

            return await _taskCompletionSource.Task;
        }

        // 条码检测成功的回调
        public void OnBarcodeDetected(Barcode barcode)
        {
            CleanupResources();
            _taskCompletionSource.SetResult(barcode.DisplayValue);
        }

        // 清理相机和检测器资源
        private void CleanupResources()
        {
            _cameraSource?.Stop();
            _cameraSource?.Release();
            _barcodeDetector?.Release();
            _dialog?.Dismiss();
        }

        // 条码处理器类,负责接收检测结果
        private class BarcodeProcessor : Java.Lang.Object, IDetector.IProcessor
        {
            private readonly IBarcodeCallback _callback;

            public BarcodeProcessor(IBarcodeCallback callback)
            {
                _callback = callback;
            }

            public void ReceiveDetections(IDetections detections)
            {
                var barcodes = detections.DetectedItems;
                if (barcodes.Size() > 0)
                {
                    var barcode = barcodes.ValueAt(0) as Barcode;
                    _callback.OnBarcodeDetected(barcode);
                }
            }

            public void Release() { }
        }
    }

    // 定义回调接口,用于传递检测结果
    public interface IBarcodeCallback
    {
        void OnBarcodeDetected(Barcode barcode);
    }
}

步骤3:iOS端实现扫描功能

3.1 配置相机权限描述

打开iOS项目的Info.plist,添加相机权限的说明(iOS要求必须说明权限用途):

<key>NSCameraUsageDescription</key>
<string>需要访问相机进行条码扫描</string>

3.2 编写扫描逻辑实现类

在iOS项目里新建BarcodeScannerImplementation类,同样实现IBarcodeScanner接口,利用AVCaptureSession实现相机预览和条码检测:

[assembly: Dependency(typeof(BarcodeScannerImplementation))]
namespace YourAppName.iOS
{
    public class BarcodeScannerImplementation : IBarcodeScanner
    {
        private TaskCompletionSource<string> _taskCompletionSource;
        private BarcodeScannerViewController _scannerViewController;

        public async Task<string> ScanBarcodeAsync()
        {
            _taskCompletionSource = new TaskCompletionSource<string>();

            // 检查相机权限
            var status = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);
            if (status == AVAuthorizationStatus.Denied || status == AVAuthorizationStatus.Restricted)
            {
                _taskCompletionSource.SetResult(null);
                return await _taskCompletionSource.Task;
            }
            else if (status == AVAuthorizationStatus.NotDetermined)
            {
                var granted = await AVCaptureDevice.RequestAccessForMediaTypeAsync(AVMediaType.Video);
                if (!granted)
                {
                    _taskCompletionSource.SetResult(null);
                    return await _taskCompletionSource.Task;
                }
            }

            // 创建自定义扫描视图控制器
            _scannerViewController = new BarcodeScannerViewController();
            _scannerViewController.BarcodeDetected += (s, e) =>
            {
                CleanupResources();
                _taskCompletionSource.SetResult(e.BarcodeValue);
            };
            _scannerViewController.Canceled += (s, e) =>
            {
                CleanupResources();
                _taskCompletionSource.SetResult(null);
            };

            // 显示扫描界面
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(_scannerViewController, true, null);

            return await _taskCompletionSource.Task;
        }

        // 清理视图资源
        private void CleanupResources()
        {
            _scannerViewController?.DismissViewController(true, null);
            _scannerViewController = null;
        }
    }

    // 自定义扫描视图控制器
    public class BarcodeScannerViewController : UIViewController
    {
        public event EventHandler<BarcodeDetectedEventArgs> BarcodeDetected;
        public event EventHandler Canceled;

        private AVCaptureSession _captureSession;
        private AVCaptureVideoPreviewLayer _previewLayer;
        private UIButton _cancelButton;

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            _captureSession = new AVCaptureSession();
            _captureSession.SessionPreset = AVCaptureSession.PresetHigh;

            // 获取后置相机设备
            var device = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
            if (device == null)
            {
                DismissViewController(true, null);
                Canceled?.Invoke(this, EventArgs.Empty);
                return;
            }

            // 创建相机输入
            NSError error;
            var input = AVCaptureDeviceInput.FromDevice(device, out error);
            if (error != null || input == null)
            {
                DismissViewController(true, null);
                Canceled?.Invoke(this, EventArgs.Empty);
                return;
            }

            _captureSession.AddInput(input);

            // 创建条码输出
            var output = new AVCaptureMetadataOutput();
            _captureSession.AddOutput(output);

            // 设置输出回调,监听条码检测结果
            output.SetMetadataObjectsDelegate(new MetadataOutputDelegate(this), DispatchQueue.MainQueue);
            output.MetadataObjectTypes = output.AvailableMetadataObjectTypes;

            // 创建预览层,显示相机画面
            _previewLayer = AVCaptureVideoPreviewLayer.FromSession(_captureSession);
            _previewLayer.Frame = View.Bounds;
            _previewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill;
            View.Layer.AddSublayer(_previewLayer);

            // 添加取消按钮
            _cancelButton = UIButton.FromType(UIButtonType.System);
            _cancelButton.SetTitle("取消", UIControlState.Normal);
            _cancelButton.Frame = new CGRect(20, View.Bounds.Height - 80, View.Bounds.Width - 40, 40);
            _cancelButton.TouchUpInside += (s, e) =>
            {
                Canceled?.Invoke(this, EventArgs.Empty);
            };
            View.AddSubview(_cancelButton);

            // 启动相机会话
            _captureSession.StartRunning();
        }

        public override void ViewWillDisappear(bool animated)
        {
            base.ViewWillDisappear(animated);
            _captureSession?.StopRunning();
        }

        // 条码检测结果的委托类
        private class MetadataOutputDelegate : AVCaptureMetadataOutputObjectsDelegate
        {
            private readonly BarcodeScannerViewController _controller;

            public MetadataOutputDelegate(BarcodeScannerViewController controller)
            {
                _controller = controller;
            }

            public override void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
            {
                if (metadataObjects == null || metadataObjects.Length == 0)
                    return;

                var barcodeObject = metadataObjects[0] as AVMetadataMachineReadableCodeObject;
                if (barcodeObject != null)
                {
                    _controller.BarcodeDetected?.Invoke(_controller, new BarcodeDetectedEventArgs(barcodeObject.StringValue));
                }
            }
        }
    }

    // 条码检测结果的事件参数类
    public class BarcodeDetectedEventArgs : EventArgs
    {
        public string BarcodeValue { get; }

        public BarcodeDetectedEventArgs(string barcodeValue)
        {
            BarcodeValue = barcodeValue;
        }
    }
}

步骤4:在Xamarin.Forms页面中调用扫描功能

现在你可以在Forms的任意页面里,通过DependencyService获取扫描服务,调用扫描功能了。比如在一个按钮的点击事件里:

private async void ScanBarcodeButton_Clicked(object sender, EventArgs e)
{
    // 获取扫描服务实例
    var scanner = DependencyService.Get<IBarcodeScanner>();
    if (scanner == null)
    {
        await DisplayAlert("提示", "当前平台不支持条码扫描", "确定");
        return;
    }

    // 启动扫描
    var barcodeResult = await scanner.ScanBarcodeAsync();
    if (!string.IsNullOrEmpty(barcodeResult))
    {
        await DisplayAlert("扫描成功", $"条码内容:{barcodeResult}", "确定");
        // 这里可以添加你的业务逻辑,比如解析条码内容
    }
    else
    {
        await DisplayAlert("提示", "未扫描到条码或已取消操作", "确定");
    }
}

一些重要的注意事项

  1. 一定要用真机测试:模拟器的相机功能有限,而且条码识别效果远不如真机
  2. 优化小条码识别:确保开启自动对焦(Android端已经设置SetAutoFocusEnabled(true),iOS端默认支持),尽量让条码填满预览画面
  3. Android初始化问题:如果BarcodeDetector初始化失败,可能是设备无法下载Google的条码识别模型,确保设备能正常访问网络,或者检查项目是否包含了必要的资源文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:46:59