求助:如何在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("提示", "未扫描到条码或已取消操作", "确定"); } }
一些重要的注意事项
- 一定要用真机测试:模拟器的相机功能有限,而且条码识别效果远不如真机
- 优化小条码识别:确保开启自动对焦(Android端已经设置
SetAutoFocusEnabled(true),iOS端默认支持),尽量让条码填满预览画面 - Android初始化问题:如果BarcodeDetector初始化失败,可能是设备无法下载Google的条码识别模型,确保设备能正常访问网络,或者检查项目是否包含了必要的资源文件
内容的提问来源于stack exchange,提问作者mustafa
相关产品推荐
相关产品推荐

