MAUI Blazor 調用相機掃碼(Android)
MAUI Blazor 與原生 MAUI 存在核心差異:其基于 WebView 嵌套實現,頁面由 Razor 組件構成,無法直接復用原生 MAUI 的二維碼掃描能力。為此,針對 Android 平臺,采用以下方案實現二維碼掃描功能。
技術選型:基于 ZXing.Net.Maui 組件構建掃描核心,結合 Android 原生 API(如 Context、Vibrator)實現平臺適配,確保相機調用、二維碼識別與觸覺反饋功能正常運行;
文件組織:在 Platforms/Android 目錄下創建 QrCodeScanner.cs,實現 IQrCodeScanner 接口,封裝平臺專屬掃描邏輯;
功能實現:從基礎能力到交互體驗分層開發,包括:
-
權限管理:通過
Permissions接口檢查并請求相機權限,處理權限拒絕場景; -
視圖構建:初始化
CameraBarcodeReaderView(相機預覽與識別)、掃描線(視覺引導)、提示文本與取消按鈕,采用AbsoluteLayout實現元素精確定位; -
流程控制:封裝掃描啟動(頁面跳轉、動畫觸發)、結果處理(震動反饋、響應返回)、資源清理(相機停止、頁面關閉)全流程,同時通過
CustomScanPage重寫返回鍵事件,確保取消邏輯統一;
異常處理:針對上下文為空、屏幕尺寸獲取失敗、導航容器未初始化等場景,定義專屬錯誤提示與響應對象,保障功能穩定性。
一、NuGet 包管理器:安裝 ZXing.Net.Maui、 ZXing.Net.Maui.Controls 包

二、修改 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
七、效果圖演示


浙公網安備 33010602011771號