MAUI Blazor 调用相机扫码(Android)

MAUI Blazor 与原生 MAUI 存在核心差异:其基于 WebView 嵌套实现,页面由 Razor 组件构成,无法直接复用原生 MAUI 的二维码扫描能力。为此,针对 Android 平台,采用以下方案实现二维码扫描功能。

  • 权限管理:通过 Permissions 接口检查并请求相机权限,处理权限拒绝场景;

  • 视图构建:初始化 CameraBarcodeReaderView(相机预览与识别)、扫描线(视觉引导)、提示文本与取消按钮,采用 AbsoluteLayout 实现元素精确定位;

  • 流程控制:封装扫描启动(页面跳转、动画触发)、结果处理(震动反馈、响应返回)、资源清理(相机停止、页面关闭)全流程,同时通过 CustomScanPage 重写返回键事件,确保取消逻辑统一;

  • 异常处理:针对上下文为空、屏幕尺寸获取失败、导航容器未初始化等场景,定义专属错误提示与响应对象,保障功能稳定性;

一、NuGet 包管理器:安装 ZXing.Net.Maui、 ZXing.Net.Maui.Controls 包

image

二、修改 AndroidManifest.xml 添加相机、震动权限

点击查看代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true" android:usesCleartextTraffic="true" android:label="xx">
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.jiajing.iotplatform.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />   
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />  
 
    <!-- 震动权限 -->
    <uses-permission android:name="android.permission.VIBRATE" />
    <!-- 相机权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
</manifest> 

三、自定义 IQrCodeScanner.cs 接口

点击查看代码
    /// <summary>
    /// 二维码扫描接口类
    /// </summary>
    public interface IQrCodeScanner
    {
        /// <summary>
        /// 启动相机扫描二维码
        /// </summary>
        /// <returns>扫描到的二维码内容</returns>
        Task<QrScannerResponse> ScanQrCodeAsync();
    }

四、在 Platforms/Android 目录下创建 QrCodeScanner.cs 文件,实现 IQrCodeScanner 接口

点击查看代码
    /// <summary>
    /// Android平台二维码扫描器实现
    /// 提供二维码扫描功能,包括相机权限管理、扫描界面展示和扫描结果处理
    /// </summary>
    public class QrCodeScanner : IQrCodeScanner
    {

        #region 私有字段

        /// <summary>
        /// 安卓上下文对象,用于访问系统功能
        /// </summary>
        private readonly Context _androidContext;

        /// <summary>
        /// 扫描视图,负责相机预览和二维码识别
        /// </summary>
        private CameraBarcodeReaderView _barcodeReaderView;

        /// <summary>
        /// 扫描线控件,提供视觉引导
        /// </summary>
        private BoxView _scanLine;

        /// <summary>
        /// 扫描提示文字控件,显示操作指引
        /// </summary>
        private Label _scanLabel;

        /// <summary>
        /// 扫描状态标识,控制扫描动画的运行与停止
        /// </summary>
        private bool _isScanning = false;

        /// <summary>
        /// 当前扫描页面引用
        /// </summary>
        private ContentPage _currentScanPage;

        /// <summary>
        /// 扫描结果任务完成源
        /// </summary>
        private TaskCompletionSource<QrScannerResponse> _resultTcs;

        /// <summary>
        /// 震动管理器,用于扫描成功后的触觉反馈
        /// </summary>
        private Vibrator _vibrator;

        #endregion


        #region 常量定义

        /// <summary>
        /// 扫描框大小(像素)
        /// </summary>
        private const int ScanAreaSize = 300;

        /// <summary>
        /// 扫描线最大高度(中间部分)
        /// </summary>
        private const int ScanLineMaxHeight = 2;

        /// <summary>
        /// 扫描提示文字与扫描框的间距
        /// </summary>
        private const int LabelMarginTop = 35;

        #endregion


        #region 错误提示文本

        private const string PermissionDeniedTip = "需要相机权限才能扫描二维码,请在系统设置中开启";
        private const string NavigationNullTip = "导航容器未初始化,无法显示扫描页面";
        private const string ViewInitFailedTip = "扫描视图初始化失败";
        private const string ContextNullTip = "安卓上下文不能为空";
        private const string ScreenSizeErrorTip = "无法获取屏幕尺寸,扫描功能无法使用";
        private const string CancelTip = "未识别,已取消扫描";

        #endregion



        /// <summary>
        /// 初始化二维码扫描器
        /// </summary>
        /// <param name="context">安卓上下文</param>
        public QrCodeScanner(Context context)
        {
            _androidContext = context;
            if (_androidContext != null)
            {
                // 初始化震动管理器
                InitializeVibrator();
            }
        }

        /// <summary>
        /// 启动二维码扫描流程
        /// </summary>
        /// <returns>扫描结果响应对象</returns>
        public async Task<QrScannerResponse> ScanQrCodeAsync()
        {
            // 基础校验
            if (_androidContext == null)
                return CreateErrorResponse(ApiCode.Exception, ContextNullTip);

            // 检查相机权限
            var permissionResult = await CheckCameraPermission();
            if (!string.IsNullOrEmpty(permissionResult))
                return CreateErrorResponse(ApiCode.Exception, permissionResult);

            // 创建任务完成源,用于异步返回扫描结果
            _resultTcs = new TaskCompletionSource<QrScannerResponse>();

            try
            {
                // 初始化扫描相关视图
                InitializeScannerViews();

                // 创建扫描页面布局
                _currentScanPage = CreateScanPage(_resultTcs);

                // 显示扫描页面
                var showPageResult = await ShowScanPageAsync(_currentScanPage);
                if (!string.IsNullOrEmpty(showPageResult))
                {
                    await StopScanning();
                    return CreateErrorResponse(ApiCode.Exception, showPageResult);
                }

                // 等待页面渲染完成后再定位扫描元素和启动动画
                await Task.Delay(300);

                // 定位扫描元素
                var positionResult = PositionScanElements();
                if (!string.IsNullOrEmpty(positionResult))
                {
                    await StopScanning();
                    return CreateErrorResponse(ApiCode.Exception, positionResult);
                }

                // 启动扫描动画
                StartScanLineAnimation();

                // 返回扫描结果
                return await _resultTcs.Task;
            }
            catch (Exception ex)
            {
                await StopScanning();
                return CreateErrorResponse(ApiCode.Exception, $"扫描初始化失败:{ex.Message}");
            }
            finally
            {
                // 清理引用
                _currentScanPage = null;
                _resultTcs = null;
            }
        }


        #region 初始化扫描相关视图组件  private void InitializeScannerViews()

        /// <summary>
        /// 初始化扫描相关视图组件
        /// </summary>
        private void InitializeScannerViews()
        {
            // 初始化条形码扫描视图
            _barcodeReaderView = new CameraBarcodeReaderView
            {
                Options = new BarcodeReaderOptions
                {
                    Formats = ZXing.Net.Maui.BarcodeFormat.QrCode,  // 只识别二维码
                    AutoRotate = true,               // 自动旋转
                    TryHarder = true                 // 提高识别准确率
                },
                IsEnabled = true,
                IsDetecting = true
            };

            // 初始化扫描线(白色)
            _scanLine = new BoxView
            {
                Color = Colors.White,
                HeightRequest = ScanLineMaxHeight,
                HorizontalOptions = LayoutOptions.Fill
            };

            // 初始化扫描提示文字
            _scanLabel = new Label
            {
                Text = "请将设备二维码对准扫描框",
                TextColor = Colors.White,
                FontSize = 16,
                HorizontalTextAlignment = TextAlignment.Center,
                VerticalTextAlignment = TextAlignment.Center,
                Opacity = 0.85  // 稍微降低不透明度,避免过于刺眼
            };
        }

        /// <summary>
        /// 创建扫描页面及其布局
        /// </summary>
        /// <param name="resultTcs">用于返回扫描结果的任务完成源</param>
        /// <returns>构建好的扫描页面</returns>
        private CustomScanPage CreateScanPage(TaskCompletionSource<QrScannerResponse> resultTcs)
        {
            // 创建主布局容器(绝对布局,用于精确定位扫描线和提示文字)
            var mainLayout = new AbsoluteLayout
            {
                VerticalOptions = LayoutOptions.FillAndExpand,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            // 添加相机视图到主布局
            AbsoluteLayout.SetLayoutBounds(_barcodeReaderView, new Rect(0, 0, 1, 1));
            AbsoluteLayout.SetLayoutFlags(_barcodeReaderView, AbsoluteLayoutFlags.All);
            mainLayout.Children.Add(_barcodeReaderView);

            // 创建取消按钮
            var cancelButton = CreateCancelButton(resultTcs);

            // 绑定扫描结果事件
            _barcodeReaderView.BarcodesDetected += async (sender, args) =>
            {
                // 确保只处理一次结果
                if (args.Results.Any() && !resultTcs.Task.IsCompleted)
                {
                    var result = args.Results[0].Value;

                    // 扫描成功后震动手机
                    VibrateOnSuccess();

                    await StopScanning();
                    resultTcs.SetResult(CreateSuccessResponse(result));
                }
            };

            // 构建自定义扫描页面
            var scanPage = new CustomScanPage(resultTcs)
            {
                BackgroundColor = Colors.Black,
                Content = new Grid
                {
                    Children = {
                        mainLayout,   // 扫码区域
                        cancelButton  // 取消按钮
                    }
                }
            };

            // 设置取消操作的委托
            scanPage.CancelAction = () => CancelScan(resultTcs);

            return scanPage;
        }

        /// <summary>
        /// 创建取消按钮并绑定事件
        /// </summary>
        /// <param name="resultTcs">用于返回扫描结果的任务完成源</param>
        /// <returns>构建好的取消按钮</returns>
        private Button CreateCancelButton(TaskCompletionSource<QrScannerResponse> resultTcs)
        {
            var cancelButton = new Button
            {
                Text = "×",
                TextColor = Colors.White,
                BackgroundColor = Color.FromArgb("#80000000"),  // 半透明黑色 
                CornerRadius = 22,  // 圆形按钮
                WidthRequest = 44,
                HeightRequest = 44,
                FontSize = 24,      // 文字大小
                FontAttributes = FontAttributes.Bold,
                HorizontalOptions = LayoutOptions.Start,
                VerticalOptions = LayoutOptions.Start,
                Margin = new Thickness(20, 30, 0, 0),
                Padding = new Thickness(0),  // 移除内边距,确保符号居中
                MinimumWidthRequest = 44,    // 设置按钮点击区域
                MinimumHeightRequest = 44
            };

            // 绑定取消按钮点击事件
            cancelButton.Clicked += (s, e) => CancelScan(resultTcs);

            return cancelButton;
        }

        #endregion


        #region 页面显示与控制  private async Task<string> ShowScanPageAsync(ContentPage scanPage)

        /// <summary>
        /// 显示扫描页面
        /// </summary>
        private async Task<string> ShowScanPageAsync(ContentPage scanPage)
        {
            try
            {
                await MainThread.InvokeOnMainThreadAsync(async () =>
                {
                    if (Application.Current?.MainPage?.Navigation == null)
                    {
                        throw new Exception(NavigationNullTip);
                    }
                    await Application.Current.MainPage.Navigation.PushModalAsync(scanPage, false);
                });
                return null;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 准确定位扫描线和提示文字位置
        /// 扫描线位于扫描框内,提示文字位于扫描框下方居中
        /// </summary>
        private string PositionScanElements()
        {
            try
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    // 获取屏幕尺寸
                    var mainPage = Application.Current?.MainPage;
                    if (mainPage == null)
                        throw new Exception(ViewInitFailedTip);

                    double screenWidth = GetScreenDimension(mainPage.Width, DeviceDisplay.MainDisplayInfo.Width);
                    double screenHeight = GetScreenDimension(mainPage.Height, DeviceDisplay.MainDisplayInfo.Height);

                    if (screenWidth <= 0 || screenHeight <= 0)
                        throw new Exception(ScreenSizeErrorTip);

                    // 计算扫描框位置(屏幕中央)
                    var scanAreaX = (screenWidth - ScanAreaSize) / 2;
                    var scanAreaY = (screenHeight - ScanAreaSize) / 2;

                    // 设置扫描线初始位置(扫描框内偏上位置)
                    SetElementLayout(_scanLine, scanAreaX, scanAreaY + 60, ScanAreaSize, ScanLineMaxHeight);

                    // 设置提示文字位置(扫描框下方居中)
                    SetElementLayout(_scanLabel, scanAreaX,
                                   scanAreaY + ScanAreaSize + LabelMarginTop,
                                   ScanAreaSize, 30);

                    // 将扫描线和提示文字添加到主布局(确保只添加一次)
                    if (_barcodeReaderView.Parent is AbsoluteLayout mainLayout)
                    {
                        AddElementToLayout(mainLayout, _scanLine);
                        AddElementToLayout(mainLayout, _scanLabel);
                    }
                });
                return null;
            }
            catch (Exception ex)
            {
                return $"扫描元素定位失败:{ex.Message}";
            }
        }

        /// <summary>
        /// 获取屏幕尺寸(处理可能的空值和0值)
        /// </summary>
        /// <param name="pageDimension">页面尺寸</param>
        /// <param name="displayDimension">显示信息尺寸</param>
        /// <returns>计算后的屏幕尺寸</returns>
        private double GetScreenDimension(double pageDimension, double displayDimension)
        {
            return pageDimension > 0
                ? pageDimension
                : displayDimension / DeviceDisplay.MainDisplayInfo.Density;
        }

        /// <summary>
        /// 设置元素在绝对布局中的位置和大小
        /// </summary>
        private void SetElementLayout(View element, double x, double y, double width, double height)
        {
            AbsoluteLayout.SetLayoutBounds(element, new Rect(x, y, width, height));
            AbsoluteLayout.SetLayoutFlags(element, AbsoluteLayoutFlags.None);
        }

        /// <summary>
        /// 将元素添加到布局中(确保只添加一次)
        /// </summary>
        private void AddElementToLayout(AbsoluteLayout layout, View element)
        {
            if (element.Parent != layout)
            {
                if (element.Parent is Layout parentLayout)
                    parentLayout.Children.Remove(element);

                layout.Children.Add(element);
            }
        }

        #endregion


        #region 扫描动画控制  private void StartScanLineAnimation()

        /// <summary>
        /// 启动扫描线动画
        /// 实现扫描线在扫描区域内上下移动的效果
        /// </summary>
        private void StartScanLineAnimation()
        {
            _isScanning = true;

            MainThread.BeginInvokeOnMainThread(async () =>
            {
                try
                {
                    // 获取屏幕尺寸和扫描区域参数
                    var (scanAreaX, scanAreaY, endY) = CalculateScanAreaParameters();
                    if (scanAreaX < 0 || scanAreaY < 0 || endY < 0)
                        return;

                    // 初始化扫描线位置和移动方向(从偏下位置开始)
                    var currentY = scanAreaY + 50;  // 初始位置偏下
                    var direction = 1;  // 1 向下移动, -1 向上移动
                    const int speed = 1; // 移动速度(像素/帧)

                    // 动画循环
                    while (_isScanning)
                    {
                        // 更新扫描线位置
                        currentY += direction * speed;

                        // 到达边界时反向移动
                        if (currentY >= endY)
                        {
                            currentY = endY;
                            direction = -1;
                        }
                        else if (currentY <= scanAreaY)
                        {
                            currentY = scanAreaY;
                            direction = 1;
                        }

                        // 应用新位置到扫描线
                        AbsoluteLayout.SetLayoutBounds(_scanLine, new Rect(
                            scanAreaX,
                            currentY,
                            ScanAreaSize,
                            ScanLineMaxHeight
                        ));

                        // 控制动画帧率(约60fps)
                        await Task.Delay(16);
                    }
                }
                catch
                {
                    // 动画异常时停止扫描,不抛出异常
                    _isScanning = false;
                }
            });
        }

        /// <summary>
        /// 计算扫描区域参数
        /// </summary>
        /// <returns>扫描区域的X坐标、Y坐标和结束Y坐标</returns>
        private (double scanAreaX, double scanAreaY, double endY) CalculateScanAreaParameters()
        {
            var mainPage = Application.Current?.MainPage;
            if (mainPage == null)
                return (-1, -1, -1);

            double screenWidth = GetScreenDimension(mainPage.Width, DeviceDisplay.MainDisplayInfo.Width);
            double screenHeight = GetScreenDimension(mainPage.Height, DeviceDisplay.MainDisplayInfo.Height);

            if (screenWidth <= 0 || screenHeight <= 0)
                return (-1, -1, -1);

            // 计算扫描区域位置和范围
            var scanAreaX = (screenWidth - ScanAreaSize) / 2;
            var scanAreaY = (screenHeight - ScanAreaSize) / 2;
            var endY = scanAreaY + ScanAreaSize - ScanLineMaxHeight;

            return (scanAreaX, scanAreaY, endY);
        }

        #endregion


        #region 停止扫描并清理资源  private async Task StopScanning()

        /// <summary>
        /// 停止扫描并清理资源
        /// </summary>
        private async Task StopScanning()
        {
            // 停止扫描动画
            _isScanning = false;

            // 停止相机检测
            if (_barcodeReaderView != null)
            {
                _barcodeReaderView.IsDetecting = false;
                _barcodeReaderView.IsEnabled = false;
            }

            // 关闭扫描页面
            try
            {
                await MainThread.InvokeOnMainThreadAsync(async () =>
                {
                    if (Application.Current?.MainPage?.Navigation != null)
                    {
                        await Application.Current.MainPage.Navigation.PopModalAsync(false);
                    }
                });
            }
            catch
            {
                // 页面关闭异常不处理,避免影响主流程
            }
        }

        #endregion


        #region 权限管理  private async Task<string> CheckCameraPermission()

        /// <summary>
        /// 检查并请求相机权限
        /// </summary>
        /// <returns>权限正常返回null,异常返回提示文本</returns>
        private async Task<string> CheckCameraPermission()
        {
            try
            {
                // 检查相机权限状态
                var status = await Permissions.CheckStatusAsync<Permissions.Camera>();

                // 已授权则直接返回
                if (status == PermissionStatus.Granted)
                    return null;

                // 未授权则请求权限
                status = await Permissions.RequestAsync<Permissions.Camera>();
                return status == PermissionStatus.Granted ? null : PermissionDeniedTip;
            }
            catch (Exception ex)
            {
                return $"相机权限请求失败:{ex.Message}";
            }
        }

        #endregion


        #region 取消扫描处理 private async void CancelScan(TaskCompletionSource<QrScannerResponse> resultTcs)

        /// <summary>
        /// 统一处理取消扫描的逻辑
        /// 无论是点击取消按钮还是按返回键都调用此方法
        /// </summary> 
        private async void CancelScan(TaskCompletionSource<QrScannerResponse> resultTcs)
        {
            if (resultTcs.Task.IsCompleted)
                return;

            await StopScanning();
            resultTcs.SetResult(CreateCancelResponse());
        }

        #endregion


        #region 震动功能初始化  private void InitializeVibrator()

        /// <summary>
        /// 安全初始化震动管理器
        /// </summary>
        private void InitializeVibrator()
        {
            try
            {
#if ANDROID
                if (_androidContext != null)
                {
                    var vibratorService = _androidContext.GetSystemService(Context.VibratorService);
                    if (vibratorService is Vibrator vibrator)
                    {
                        _vibrator = vibrator;
                    }
                }
#endif
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"震动服务初始化失败: {ex.Message}");
                _vibrator = null;
            }
        }

        /// <summary>
        /// 扫描成功时震动手机
        /// </summary>
        private void VibrateOnSuccess()
        {
            try
            {
#if ANDROID
                if (_vibrator != null && HasVibrator())
                {
                    // 震动100毫秒
                    if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
                    {
                        _vibrator.Vibrate(VibrationEffect.CreateOneShot(100, VibrationEffect.DefaultAmplitude));
                    }
                    else
                    {
                        _vibrator.Vibrate(100);
                    }
                }
#endif
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"震动失败: {ex.Message}");
                // 震动失败不影响主要功能,静默处理
            }
        }

        /// <summary>
        /// 检查设备是否支持震动
        /// </summary>
        private bool HasVibrator()
        {
#if ANDROID
            return _vibrator?.HasVibrator == true;
#else
                return false;
#endif
        }

        #endregion


        #region 响应对象创建方法 private QrScannerResponse CreateSuccessResponse(string data)

        /// <summary>
        /// 创建成功响应
        /// </summary>
        /// <param name="data">二维码数据</param>
        /// <returns>成功响应对象</returns>
        private QrScannerResponse CreateSuccessResponse(string data)
        {
            return new QrScannerResponse
            {
                Code = ApiCode.Success,
                Msg = "扫描成功",
                Data = data
            };
        }

        /// <summary>
        /// 创建取消响应
        /// </summary>
        /// <returns>取消响应对象</returns>
        private QrScannerResponse CreateCancelResponse()
        {
            return new QrScannerResponse
            {
                Code = ApiCode.Exception,
                Msg = CancelTip,
                Data = CancelTip
            };
        }

        /// <summary>
        /// 创建错误响应
        /// </summary>
        /// <param name="code">错误代码</param>
        /// <param name="message">错误信息</param>
        /// <returns>错误响应对象</returns>
        private QrScannerResponse CreateErrorResponse(ApiCode code, string message)
        {
            return new QrScannerResponse
            {
                Code = code,
                Msg = message,
                Data = null
            };
        }

        #endregion

    }


    /// <summary>
    /// 自定义扫描页面
    /// </summary>
    public class CustomScanPage : ContentPage
    {
        private readonly TaskCompletionSource<QrScannerResponse> _resultTcs;

        public CustomScanPage(TaskCompletionSource<QrScannerResponse> resultTcs)
        {
            _resultTcs = resultTcs;
        }

        protected override bool OnBackButtonPressed()
        {
            // 调用取消扫描的逻辑
            CancelAction?.Invoke();
            return true; // 表示已处理返回键事件,不执行默认行为
        }

        public Action CancelAction { get; set; }
    }

QrScannerResponse.cs 识别结果返回类

    /// <summary>
    /// 识别结果,返回类
    /// </summary>
    public class QrScannerResponse
    {
        /// <summary>
        /// 状态码
        /// </summary>
        public ApiCode Code { get; set; }

        /// <summary>
        /// 提示信息
        /// </summary>
        public string Msg { get; set; }

        /// <summary>
        /// 识别数据
        /// </summary>
        public string Data { get; set; }
    }
     
    /// <summary>
    /// 状态码,枚举类
    /// </summary>
    public enum ApiCode : int
    {
        /// <summary>
        /// 成功
        /// </summary>
        Success = 10001, 
        /// <summary>
        /// 异常
        /// </summary>
        Exception = 10002, 
        /// <summary>
        /// 无效
        /// </summary>
        InValidData = 10003, 
    }

五、修改 MauiProgram.cs 文件,注册相关服务

#if ANDROID 是必不可少的,该条件编译指令能确保相关逻辑只在 Android 环境中执行.

点击查看代码
// Add the using to the top
using ZXing.Net.Maui.Controls;
 
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
                .UseMauiApp<App>()
                .UseBarcodeReader(); // Make sure to add this line

    #if ANDROID    
        // 注册Android扫描服务(注入上下文)
        builder.Services.AddSingleton<IQrCodeScanner>(sp =>
            new Platforms.Android.QrCodeScanner(
                Android.App.Application.Context // 应用级上下文,避免内存泄漏
            )
        );
    #endif 

        return builder.Build();
    } 
}

六、通过调用 QrCodeScanner.ScanQrCodeAsync() 方法识别二维码

点击查看代码
// 注入QR识别组件服务
[Inject] private IQrCodeScanner QrCodeScanner { get; set; }
 
 

#region 调用ScanQrCodeAsync实现QR码识别功能
/// <summary>
/// 调用二维码扫描器
/// </summary>
public void OnQrCodeScanner()
{ 
    // 扫描QR码并处理结果
    QrCodeScanner.ScanQrCodeAsync()
        .ContinueWith(async task =>
        {
            // 检查任务是否成功完成
            if (!task.IsCompletedSuccessfully || task.Result == null)
                return;

            QrScannerResponse result = task.Result;

            // 检查扫描结果是否成功
            if (result.Code != ApiCode.Success)
            {
                await dialogService.InfoSnackbarAsync($"扫码失败:{result.Msg}")
                    .ConfigureAwait(false);
                return;
            }

            try
            {
                // 解析二维码数据为JObject
                if (string.IsNullOrEmpty(result.Data))
                {
                    await dialogService.ErrorSnackbarAsync("二维码数据为空")
                        .ConfigureAwait(false);
                    return;
                }
                // 添加数据处理逻辑
                JObject jObject = JObject.Parse(result.Data);
                string xx= jObject["xx"]?.ToString() ?? string.Empty; 
            }
            catch (Exception)
            {
                // 解析失败时显示原始内容
                await dialogService.InfoSnackbarAsync($"识别内容:{result.Data}")
                    .ConfigureAwait(false);
            }
        }, TaskScheduler.FromCurrentSynchronizationContext());
}
#endregion

七、效果图演示

image

posted @ 2025-09-19 12:31  笺上知微  阅读(118)  评论(0)    收藏  举报