跳至页脚内容
USING IRONBARCODE

MAUI 条码扫描器与IronBarcode的分步指南

移动应用程序越来越依赖条形码扫描来进行库存管理、销售点系统和产品跟踪。 构建一个MAUI条码扫描器可以让您将条码检测直接集成到您的.NET MAUI应用程序中,将相机馈送与图像文件处理结合起来,以检测二维码、数据矩阵和其他条码格式。 虽然许多条码库专注于相机预览,但IronBarcode即使在具有挑战性的条件下也能准确读取条码——倾斜的角度、光线不足和损坏的标签,所有这些都无需额外配置即可处理。

本指南将逐步介绍如何使用IronBarcode在.NET MAUI项目中实现条形码扫描。 到最后,您将能够从单个图像文件中扫描多个条形码,从设备摄像头捕获条形码,并自信地将该库集成到您自己的跨平台项目中。

构建 MAUI 条形码扫描器需要哪些先决条件?

开始之前,请确保您的开发环境已准备就绪:

  • 已安装.NET MAUI工作负载的Visual Studio 2022 (版本 17.8 或更高版本)。
  • .NET 10 SDK -- 从.NET官方网站下载 -具备基本的 C# 知识——熟悉 async/await 模式会有帮助
  • 用于相机测试的物理设备或模拟器 IronBarcode许可证——提供免费试用版供评估

在创建项目之前确保 Visual Studio 已安装 MAUI 工作负载,可以节省后续大量的故障排除时间。 您可以在 Visual Studio 安装程序的"单个组件"下搜索".NET多平台应用程序 UI 开发"来验证这一点。

了解IronBarcode如何融入 MAUI

.NET MAUI为您提供一个可同时面向 Android、iOS、macOS 和 Windows 的代码库。 在这种环境下进行条形码扫描的挑战在于,每个平台处理摄像头访问的方式都不同。 IronBarcode通过在图像处理层进行处理来解决这个问题——您通过 MAUI 的 MediaPicker 捕获图像,然后将字节交给IronBarcode进行分析。

这种关注点分离的做法可以保持代码简洁,避免使用特定于平台的条形码 SDK。 IronBarcode 的离线处理模型也意味着条形码数据永远不会离开设备,这对于受监管行业的应用来说非常重要。

支持的 BarCode 格式

IronBarcode可读取多种格式,包括:

IronBarcode支持的条码格式
格式类别 格式 常见用例
一维线性 Code 128、Code 39、EAN-13、UPC-A、ITF 零售、物流、医疗保健
二维矩阵 二维码、数据矩阵、阿兹特克、PDF417 移动支付、票务、制造业
邮政 美国邮政、英国皇家邮政、德国邮政 运输和邮政服务
专业 MaxiCode、GS1、MicroPDF417 供应链、运输、包裹

如何设置 MAUI 条形码扫描项目?

首先在 Visual Studio 2022 中创建一个新的.NET MAUI应用程序项目。将其命名为 BarcodeScannerApp,并选择.NET 10 作为目标框架。 Visual Studio 会生成标准的 MAUI 项目结构,其中包含 Android、iOS、macOS 和 Windows 等平台特定的文件夹。

通过NuGet安装IronBarcode

打开NuGet包管理器控制台并运行:

Install-Package BarCode

或者,在解决方案资源管理器中右键单击您的项目,选择"管理NuGet程序包",搜索 IronBarCode,然后安装最新的稳定版本。 专门针对.NET MAUI项目,IronBarcode 的NuGet包包含了所有必要的本机依赖项。

激活您的许可证

安装完成后,请在应用程序生命周期的早期阶段使用您的许可证密钥激活IronBarcode 。 最佳位置是在应用程序构建器运行之前,即 MauiProgram.cs 处:

IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

从 IronSoftware 网站获取免费试用许可证密钥。试用密钥允许您在开发过程中不受时间限制地评估所有功能,但输出结果可能包含试用水印,直到您申请完整许可证为止。

如何配置安卓和iOS系统的相机权限?

特定平台的相机权限对于条码扫描功能至关重要。 每个平台都需要在其清单文件中进行特定配置,然后 MediaPicker.CapturePhotoAsync() 才能成功。

Android权限

编辑 Platforms/Android/AndroidManifest.xml 以声明摄像头访问权限:

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

android.permission.CAMERA 条目请求用户在运行时授予权限。 uses-feature 声明告知 Google Play 商店,您的应用需要相机硬件和自动对焦功能。 如果没有这些,Android 设备可能会授予权限请求,但仍然会在内部阻止相机访问。

对于 Android 13 及更高版本(API 级别 33+),您可能还需要使用 MainActivity.cs 来处理细粒度的媒体权限。 MAUI MediaPicker 抽象层会自动处理大部分此类操作,但建议在发布前进行物理设备测试。

iOS权限

修改 Platforms/iOS/Info.plist,使其包含相机使用说明:

<key>NSCameraUsageDescription</key>
<string>This app requires camera access to scan barcodes</string>
<key>NSCameraUsageDescription</key>
<string>This app requires camera access to scan barcodes</string>
XML

iOS 要求对每一项涉及隐私的权限都提供易于理解的解释。 如果缺少此描述或描述含糊不清,苹果应用商店的审核流程将拒绝您的应用。 该文本出现在应用程序首次请求访问相机时向用户显示的系统权限对话框中。

对于 iPadOS,如果您计划让用户除了使用实时相机外,还可以扫描已保存照片中的条形码,请考虑添加 NSPhotoLibraryUsageDescription

Windows 和 macOS

对于 Windows Desktop 和 macOS 目标平台,相机访问权限分别通过应用程序清单文件和授权文件进行管理。 MAUI 框架在模板级别处理了大部分此类问题,但请验证 Windows 上的 Package.appxmanifest 是否包含网络摄像头设备功能。

如何创建条形码扫描器界面?

MainPage.xaml 中设计一个用户界面,使用户在扫描过程中获得清晰的反馈。 简洁而实用的布局包括图像预览、结果显示区域和扫描触发按钮:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BarcodeScannerApp.MainPage"
             Title="Barcode Scanner">
    <VerticalStackLayout Padding="20" Spacing="20">
        <Label Text="Point the camera at a barcode"
               FontSize="16"
               HorizontalOptions="Center"
               TextColor="#555555" />
        <Image x:Name="CapturedImage"
               HeightRequest="300"
               Aspect="AspectFit"
               BackgroundColor="#F0F0F0" />
        <Label x:Name="ResultLabel"
               Text="Tap Scan to begin"
               FontSize="18"
               HorizontalOptions="Center"
               FontAttributes="Bold" />
        <Label x:Name="FormatLabel"
               Text=""
               FontSize="13"
               HorizontalOptions="Center"
               TextColor="#888888" />
        <Button Text="Scan Barcode"
                Clicked="OnScanClicked"
                BackgroundColor="#007ACC"
                TextColor="White"
                CornerRadius="8"
                HeightRequest="50" />
        <Button Text="Load from Gallery"
                Clicked="OnPickFromGalleryClicked"
                BackgroundColor="#5C5C5C"
                TextColor="White"
                CornerRadius="8"
                HeightRequest="50" />
    </VerticalStackLayout>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BarcodeScannerApp.MainPage"
             Title="Barcode Scanner">
    <VerticalStackLayout Padding="20" Spacing="20">
        <Label Text="Point the camera at a barcode"
               FontSize="16"
               HorizontalOptions="Center"
               TextColor="#555555" />
        <Image x:Name="CapturedImage"
               HeightRequest="300"
               Aspect="AspectFit"
               BackgroundColor="#F0F0F0" />
        <Label x:Name="ResultLabel"
               Text="Tap Scan to begin"
               FontSize="18"
               HorizontalOptions="Center"
               FontAttributes="Bold" />
        <Label x:Name="FormatLabel"
               Text=""
               FontSize="13"
               HorizontalOptions="Center"
               TextColor="#888888" />
        <Button Text="Scan Barcode"
                Clicked="OnScanClicked"
                BackgroundColor="#007ACC"
                TextColor="White"
                CornerRadius="8"
                HeightRequest="50" />
        <Button Text="Load from Gallery"
                Clicked="OnPickFromGalleryClicked"
                BackgroundColor="#5C5C5C"
                TextColor="White"
                CornerRadius="8"
                HeightRequest="50" />
    </VerticalStackLayout>
</ContentPage>
XML

该布局提供了两种扫描路径:使用相机拍摄新照片和从图库中选择现有图像。 对于用户预先拍摄条形码或通过电子邮件接收图像的工作流程来说,这一点很重要。 FormatLabel 显示检测到的条形码格式以及解码值,这有助于调试和用户验证。

添加扫描状态反馈

为了获得更流畅的体验,可以考虑将扫描按钮包裹在 ActivityIndicator 中,这样在处理过程中就会显示出来。 IronBarcode 的异步 API 使这变得很简单——你可以在调用 BarcodeReader.ReadAsync 之前设置 IsRunning = true,并在 finally 块中重置它。

如何实现条形码阅读器功能?

MainPage.xaml.cs 中实现核心扫描逻辑。 以下代码同时处理相机拍摄和图库选择,并具有适当的异步模式和错误处理:

using IronBarCode;
using IronSoftware.Drawing;

namespace BarcodeScannerApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private async void OnScanClicked(object sender, EventArgs e)
    {
        await ScanFromSource(() => MediaPicker.Default.CapturePhotoAsync());
    }

    private async void OnPickFromGalleryClicked(object sender, EventArgs e)
    {
        await ScanFromSource(() => MediaPicker.Default.PickPhotoAsync());
    }

    private async Task ScanFromSource(Func<Task<FileResult?>> sourceFunc)
    {
        try
        {
            var photo = await sourceFunc();
            if (photo is null) return;

            using var stream = await photo.OpenReadAsync();
            using var memoryStream = new MemoryStream();
            await stream.CopyToAsync(memoryStream);
            var imageBytes = memoryStream.ToArray();

            // Show the captured image in the UI
            CapturedImage.Source = ImageSource.FromStream(() =>
                new MemoryStream(imageBytes));

            // Process with IronBarcode
            var bitmap = AnyBitmap.FromBytes(imageBytes);
            var options = new BarcodeReaderOptions
            {
                Speed = ReadingSpeed.Balanced,
                ExpectMultipleBarcodes = false
            };

            var results = await BarcodeReader.ReadAsync(bitmap, options);

            if (results.Any())
            {
                var first = results.First();
                ResultLabel.Text = $"Value: {first.Value}";
                FormatLabel.Text = $"Format: {first.BarcodeType}";
            }
            else
            {
                ResultLabel.Text = "No barcode detected";
                FormatLabel.Text = string.Empty;
            }
        }
        catch (FeatureNotSupportedException)
        {
            await DisplayAlert("Unsupported",
                "Camera is not available on this device.", "OK");
        }
        catch (PermissionException)
        {
            await DisplayAlert("Permission Required",
                "Please grant camera permission in Settings.", "OK");
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error",
                $"Scanning failed: {ex.Message}", "OK");
        }
    }
}
using IronBarCode;
using IronSoftware.Drawing;

namespace BarcodeScannerApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private async void OnScanClicked(object sender, EventArgs e)
    {
        await ScanFromSource(() => MediaPicker.Default.CapturePhotoAsync());
    }

    private async void OnPickFromGalleryClicked(object sender, EventArgs e)
    {
        await ScanFromSource(() => MediaPicker.Default.PickPhotoAsync());
    }

    private async Task ScanFromSource(Func<Task<FileResult?>> sourceFunc)
    {
        try
        {
            var photo = await sourceFunc();
            if (photo is null) return;

            using var stream = await photo.OpenReadAsync();
            using var memoryStream = new MemoryStream();
            await stream.CopyToAsync(memoryStream);
            var imageBytes = memoryStream.ToArray();

            // Show the captured image in the UI
            CapturedImage.Source = ImageSource.FromStream(() =>
                new MemoryStream(imageBytes));

            // Process with IronBarcode
            var bitmap = AnyBitmap.FromBytes(imageBytes);
            var options = new BarcodeReaderOptions
            {
                Speed = ReadingSpeed.Balanced,
                ExpectMultipleBarcodes = false
            };

            var results = await BarcodeReader.ReadAsync(bitmap, options);

            if (results.Any())
            {
                var first = results.First();
                ResultLabel.Text = $"Value: {first.Value}";
                FormatLabel.Text = $"Format: {first.BarcodeType}";
            }
            else
            {
                ResultLabel.Text = "No barcode detected";
                FormatLabel.Text = string.Empty;
            }
        }
        catch (FeatureNotSupportedException)
        {
            await DisplayAlert("Unsupported",
                "Camera is not available on this device.", "OK");
        }
        catch (PermissionException)
        {
            await DisplayAlert("Permission Required",
                "Please grant camera permission in Settings.", "OK");
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error",
                $"Scanning failed: {ex.Message}", "OK");
        }
    }
}
$vbLabelText   $csharpLabel

此实现使用共享的 ScanFromSource 辅助方法,以避免在相机和图库路径之间重复图像处理逻辑。 AnyBitmap.FromBytes 方法可自动处理 JPEG、PNG、WebP 和其他常见图像格式——无需手动检测格式。

结果对象公开了 first.BarcodeType(格式枚举)和 first.BarcodeImage(检测到的条形码区域的裁剪图像)等属性。 请参阅BarcodeResult 类文档以获取完整列表。

测试您的扫描实施

代码编写完成后,就可以使用标准条形码进行测试了:

如何使用IronBarcode创建 MAUI 条形码扫描器:图 2 - 输入测试条形码

扫描后,解码值将显示在屏幕上:

如何使用IronBarcode创建MAUI条形码扫描器:图3 - 扫描的条形码值

如何配置高级扫描选项?

IronBarcode公开了一个 BarcodeReaderOptions 对象,您可以根据具体用例微调检测行为。 了解这些选项有助于您根据应用程序的需求,在速度和准确性之间取得平衡。

针对特定条形码类型

指定所需的条形码类型可以显著缩短处理时间,因为IronBarcode会跳过不需要的格式检查:

var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Balanced,
    ExpectMultipleBarcodes = true,
    ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128
};

var results = await BarcodeReader.ReadAsync(bitmap, options);
var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Balanced,
    ExpectMultipleBarcodes = true,
    ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128
};

var results = await BarcodeReader.ReadAsync(bitmap, options);
$vbLabelText   $csharpLabel

如何使用IronBarcode创建 MAUI 条形码扫描器:图 4 - 从同一图像扫描多个条形码

设置 ExpectMultipleBarcodes = true 指示IronBarcode在找到第一个结果后继续扫描,这对于仓库工作流程至关重要,因为一张装箱单可能包含十几个条形码。

阅读速度选项

ReadingSpeed 枚举提供了四个级别:BalancedQuickScan。 在条形码清晰明亮、打印量大的场景下,可以使用 QuickScan。 当扫描低分辨率相机拍摄的图像或部分损坏的标签时,请切换到 DetailedExtremeDetail

有关调整 BarcodeReaderOptions 的更多信息,包括图像校正滤波器和置信度阈值,文档提供了详细的示例。

图像校正和预处理

IronBarcode内置图像校正功能,可自动处理旋转、倾斜或光线不足的条形码。 您还可以通过 BarcodeReaderOptions.ImageFilters 手动应用预处理过滤器:

var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Detailed,
    ImageFilters = new ImageFilterCollection
    {
        new SharpenFilter(),
        new ContrastFilter(1.2f)
    }
};
var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Detailed,
    ImageFilters = new ImageFilterCollection
    {
        new SharpenFilter(),
        new ContrastFilter(1.2f)
    }
};
$vbLabelText   $csharpLabel

当应用程序面向配备低质量摄像头传感器的旧款 Android 设备,或者用户可能在仓库或户外环境等光线不足的条件下拍摄条形码时,预处理滤镜尤其有用。

您如何处理常见的故障排除场景?

即使使用配置良好的 MAUI 条形码扫描器,也可能出现因设备特定行为、图像质量问题或平台限制而导致的问题。

相机无法打开

如果相机启动失败,请验证 AndroidManifest.xmlInfo.plist 中是否正确声明了权限。 然后从全新构建版本重新部署应用程序,而不是进行热重载。 在 Android 系统上,还要检查您的测试设备是否使用非标准相机配置——某些具有多个摄像头的设备需要明确选择镜头。

在模拟器上,不支持 MediaPicker.CapturePhotoAsync()。 务必在真机上测试相机功能。模拟器支持图库选择功能(PickPhotoAsync),您可以使用预加载的图片进行基本的用户界面测试。

扫描准确率差

如果IronBarcode没有返回任何结果或返回错误值,请尝试以下调整:

  • Speed 增加到 ReadingSpeed.DetailedExtremeDetail
  • SharpenFilterContrastFilter 添加到图像过滤器管道中
  • 确保采集图像的分辨率至少为 720p;分辨率过低会导致数据矩阵等高密度格式的数据检测出现漏检。
  • 检查您的 ExpectBarcodeTypes 掩码中是否包含条形码类型

IronBarcode故障排除指南涵盖了针对特定格式问题的额外诊断步骤。

内存管理

加载到 MemoryStream 中的大型相机图像会消耗大量内存。 始终在所有流对象上使用 using 语句以确保释放。 对于用户按顺序扫描多个项目的连续扫描工作流程,处理完成后也应显式调用 bitmap.Dispose(),而不是等待垃圾回收器。

对于堆空间有限的 Android 设备,如果扫描的是清晰、高对比度的条形码,不需要全分辨率即可准确解码,则应考虑在将图像传递给IronBarcode之前对其进行降采样。

平台特定的 iOS 行为

在 iOS 系统中,当你的应用首次请求相机权限时,系统会显示一次性对话框。 如果用户拒绝,则后续对 CapturePhotoAsync 的调用将抛出 PermissionException。 处理此案例的方法是引导用户进入设置,您可以使用 AppInfo.ShowSettingsUI() 来完成此操作。

在提交到 App Store 之前,请确认 NSCameraUsageDescription 存在于 Info.plist 中。 缺少隐私字符串会导致系统自动拒绝审核,审核团队不会给出详细解释。 请查阅Apple 人机交互指南,了解有关相机访问权限的最佳实践,包括权限请求的时机和消息传递方式。

下一步计划是什么?

现在您已经拥有一个可正常运行的 MAUI 条形码扫描器和IronBarcode,根据您的应用程序需求,有几种方法可供选择:

-生成条形码-- IronBarcode包含一个 条形码生成 API ,可根据字符串数据创建二维码、Code 128 标签和其他格式的条形码。 -批量扫描-- 使用 BarcodeReader.ReadAsyncExpectMultipleBarcodes = true 在循环中处理多个图像文件; 请参阅批量扫描示例

  • PDF 条形码提取-- IronBarcode可以使用相同的 BarcodeReader 类读取嵌入在 PDF 文档中的条形码; 查阅PDF 条形码读取文档 -样式和品牌——使用颜色、徽标和注释自定义生成的条形码的视觉输出; 查看条形码样式选项 -其他 IronSoftware 产品——如果您的 MAUI 应用还需要 PDF 生成、OCR 或电子表格支持,请探索完整的Iron Suite套件,以获得一致的跨平台功能。

首先申请免费试用许可证,在评估期内可以不受限制地进行部署。 有关生产许可选项和批量定价,请访问IronBarcode定价页面IronBarcode文档门户IronBarcode GitHub存储库提供了更多代码示例和社区支持。

常见问题解答

使用 IronBarcode 进行 MAUI 条码扫描器的优势是什么?

IronBarcode在图像层处理条形码,可离线工作,支持 30 多种格式,无需额外配置即可处理倾斜角度和光线不足等具有挑战性的条件。

IronBarcode能否检测一张图片中的多个条形码?

IronBarcode的。在 BarcodeReaderOptions 中设置 ExpectMultipleBarcodes = true,然后调用 BarcodeReader.ReadAsync。IronBarcode 会将所有检测到的条形码返回到结果集合中。

如何在MAUI中配置Android和iOS的相机权限?

对于 Android 系统,请在 AndroidManifest.xml 文件中添加 CAMERA uses-permission 和 uses-feature 元素。对于 iOS 系统,请在 Info.plist 文件中添加 NSCameraUsageDescription 元素,并添加易于理解的解释。

IronBarcode是否支持离线条码扫描?

是的。IronBarcode 完全在设备端处理图像,无需将数据发送到外部服务器,这对于注重隐私的应用来说非常重要。

IronBarcode在MAUI中支持哪些条形码格式?

IronBarcode支持 QR 码、Code 128、Code 39、EAN-13、UPC-A、Data Matrix、PDF417、Aztec、MaxiCode 等多种条码格式。您可以通过 BarcodeEncoding 参数指定支持的条码格式。

如何提高低质量图像的扫描精度?

将 ReadingSpeed 设置为 Detailed 或 ExtremeDetail,将 SharpenFilter 和 ContrastFilter 添加到 ImageFilters,并确保捕获的图像分辨率至少为 720p。

IronBarcode能否在MAUI应用程序中读取PDF文件中的条形码?

是的。IronBarcode 的 BarcodeReader 类可以使用与处理图像文件相同的 ReadAsync API 提取嵌入在 PDF 文档中的条形码。

当相机访问权限被拒绝时,我该如何处理 PermissionException 异常?

在 try/catch 块中捕获 PermissionException,并调用 AppInfo.ShowSettingsUI() 将用户引导至其设备设置,以便他们可以重新启用相机访问权限。

Jordi Bardia
软件工程师
Jordi 最擅长 Python、C# 和 C++,当他不在 Iron Software 利用这些技能时,他就在游戏编程。分享产品测试、产品开发和研究的责任,Jordi 在持续的产品改进中增加了巨大的价值。多样的经验使他面临挑战并保持投入,他表示这是在 Iron Software 工作的最喜欢的方面之一。Jordi 在佛罗里达州迈阿密长大,并在佛罗里达大学学习计算机科学和统计学。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我