IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 如何使用.NET MAUI开发Android扫码应用 -> 正文阅读

[移动开发]如何使用.NET MAUI开发Android扫码应用

.NET MAUI是Xamarin的进化版,如果你已经用了Xamarin,那么可以尝试把工程移植到.NET MAUI。这篇文章分享下我的移植心得。

从Xamarin.Forms迁移到.NET MAUI

我之前用Xamarin.Forms写了一个适用于Android和iOS的一维码,二维码扫描程序:https://github.com/yushulx/xamarin-forms-barcode-qrcode-scanner

微软官方在GitHub上发布了一个移植教程,但我觉得还是创建一个新的.NET MAUI比较好,这样可以避免一下子出现大量的编译错误。

创建.NET MAUI工程

要创建.NET MAUI工程,你需要安装Visual Studio 2022 Preview版本,稳定版不包含工程模板。

.NET MAUI工程创建之后,包含了Windows, macOS, Tizen, Android和iOS的代码和支持框架。为了简洁干净,删除不必要的平台,只保留Android和iOS。

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFrameworks>net6.0-android;net6.0-ios</TargetFrameworks>
		<OutputType>Exe</OutputType>
		<RootNamespace>BarcodeQrScanner</RootNamespace>
		<UseMaui>true</UseMaui>
		<SingleProject>true</SingleProject>
</Project>

通过NuGet安装依赖库

我们需要用到两个重要的库,一个是SkiaSharp,另一个是Dynamsoft Barcode Reader

SkiaSharp用于Xamarin.Forms和.NET MAUI的包名是不同的。使用Xamarin.Forms安装SkiaSharp.Views.Forms,使用.NET MAUI安装SkiaSharp.Views.Maui.Controls

Dynamsoft Barcode Reader没有针对框架定制。

请添加图片描述
安装之后发现,Android可以用,而iOS会出现兼容性错误。

请添加图片描述
因此移植之后,Android上可以正常扫码,而iOS上只能打开摄像头。iOS上相关的扫码代码全部注释掉了,这部分可以用别的库替代,或者等待SDK发布更新。

代码

根据在线文档,要调用平台相关的代码需要用到partical classpartial method

我们在公共代码里定义:

public partial class BarcodeQRCodeService
{
    public partial void InitSDK(string license);
    public partial BarcodeQrData[] DecodeFile(string filePath);
}

然后在Android和iOS的平台代码里去实现接口。

public class DBRLicenseVerificationListener : Java.Lang.Object, IDBRLicenseVerificationListener
    {
        public void DBRLicenseVerificationCallback(bool isSuccess, Java.Lang.Exception error)
        {
            if (!isSuccess)
            {
                System.Console.WriteLine(error.Message);
            }
        }
    }
    
public partial class BarcodeQRCodeService
{
    public partial void InitSDK(string license)
    {
        BarcodeReader.InitLicense(license, new DBRLicenseVerificationListener());
        reader = new BarcodeReader();
    }

    public partial BarcodeQrData[] DecodeFile(string filePath)
    {
        BarcodeQrData[] output = null;
            try
            {
                PublicRuntimeSettings settings = reader.RuntimeSettings;
                settings.ExpectedBarcodesCount = 512;
                reader.UpdateRuntimeSettings(settings);
                TextResult[] results = reader.DecodeFile(filePath);
                if (results != null && results.Length > 0)
                {
                    output = new BarcodeQrData[results.Length];
                    int index = 0;
                    foreach (TextResult result in results)
                    {
                        BarcodeQrData data = new BarcodeQrData();
                        data.text = result.BarcodeText;
                        data.format = result.BarcodeFormatString;
                        LocalizationResult localizationResult = result.LocalizationResult;
                        data.points = new SKPoint[localizationResult.ResultPoints.Count];
                        int pointsIndex = 0;
                        foreach (Com.Dynamsoft.Dbr.Point point in localizationResult.ResultPoints)
                        {
                            SKPoint p = new SKPoint();
                            p.X = point.X;
                            p.Y = point.Y;
                            data.points[pointsIndex++] = p;
                        }
                        output[index++] = data;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return output;
    }
}

以上这部分代码用于拍照识别一维码,二维码。

要实现视频流扫码,需要用到Custom Renderer。这部分可以完全参照Xamarin.Forms,不同的是namepsace需要修改。

Android

using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers;

[assembly: ExportRenderer(typeof(BarcodeQrScanner.CameraPreview), typeof(CameraPreviewRenderer))]
namespace BarcodeQrScanner.Platforms.Android
{
    public class CameraPreviewRenderer : FrameLayout, IVisualElementRenderer, IViewRenderer, TextureView.ISurfaceTextureListener, Camera.IPreviewCallback, Handler.ICallback
    {
        CameraPreview element;
        VisualElementTracker visualElementTracker;
        VisualElementRenderer visualElementRenderer;
        int? defaultLabelFor;

        public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
        public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;

        BarcodeReader barcodeReader = new BarcodeReader();

        CameraPreview Element
        {
            get => element;
            set
            {
                if (element == value)
                {
                    return;
                }

                var oldElement = element;
                element = value;
                OnElementChanged(new ElementChangedEventArgs<CameraPreview>(oldElement, element));
            }
        }

        public CameraPreviewRenderer(Context context) : base(context)
        {
            visualElementRenderer = new VisualElementRenderer(this);
        }

        void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
        {
            if (e.OldElement != null)
            {
                e.OldElement.PropertyChanged -= OnElementPropertyChanged;
            }
            if (e.NewElement != null)
            {
                this.EnsureId();

                e.NewElement.PropertyChanged += OnElementPropertyChanged;

                ElevationHelper.SetElevation(this, e.NewElement);
            }

            ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));

            try
            {
                SetupUserInterface();
                AddView(view);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(@"			ERROR: ", ex.Message);
            }
        }

        void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            ElementPropertyChanged?.Invoke(this, e);
        }
}

iOS

using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Handlers.Compatibility;

[assembly: ExportRenderer(typeof(BarcodeQrScanner.CameraPreview), typeof(CameraPreviewRenderer))]
namespace BarcodeQrScanner.Platforms.iOS
{
    public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview>
    {
        UICameraPreview uiCameraPreview;

        protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                uiCameraPreview.Tapped -= OnCameraPreviewTapped;
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    uiCameraPreview = new UICameraPreview(e.NewElement);
                    SetNativeControl(uiCameraPreview);
                }
                // Subscribe
                uiCameraPreview.Tapped += OnCameraPreviewTapped;
            }
        }

        void OnCameraPreviewTapped(object sender, EventArgs e)
        {
            if (uiCameraPreview.IsPreviewing)
            {
                uiCameraPreview.CaptureSession.StopRunning();
                uiCameraPreview.IsPreviewing = false;
            }
            else
            {
                uiCameraPreview.CaptureSession.StartRunning();
                uiCameraPreview.IsPreviewing = true;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                Control.CaptureSession.Dispose();
                Control.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

在Content Page中使用SkiaSharp来绘制overlay。对应的namespace要改成xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"

<?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"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"
             xmlns:local="clr-namespace:BarcodeQrScanner;assembly=BarcodeQrScanner"
             x:Class="BarcodeQrScanner.CameraPage"
             Title="CameraPage">
    <Grid x:Name="scannerView" Margin="0">
        <local:CameraPreview 
            x:Name="cameraView"
            Camera="Rear"
            ScanMode="Multiple"
			HorizontalOptions="FillAndExpand"
			VerticalOptions="FillAndExpand" 
            ResultReady="CameraPreview_ResultReady"/>
        <Label FontSize="18"
                FontAttributes="Bold"
               TextColor="Blue"
                x:Name="ResultLabel"
                   Text="                                        "
                HorizontalOptions="Center"
               VerticalOptions="Center" />
        <skia:SKCanvasView x:Name="canvasView"
                           Margin="0"
                           HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

    </Grid>
</ContentPage>

最后还有一步是在MauiProgram.cs中配置SkiaSharpCustom Renderer

using Microsoft.Maui.Controls.Compatibility.Hosting;
using SkiaSharp.Views.Maui.Controls.Hosting;

namespace BarcodeQrScanner;

public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder.UseSkiaSharp()
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			}).UseMauiCompatibility()
            .ConfigureMauiHandlers((handlers) => {
                
#if ANDROID
                handlers.AddCompatibilityRenderer(typeof(CameraPreview), typeof(BarcodeQrScanner.Platforms.Android.CameraPreviewRenderer));
#endif

#if IOS
				                        handlers.AddHandler(typeof(CameraPreview), typeof(BarcodeQrScanner.Platforms.iOS.CameraPreviewRenderer));
#endif
			});


		return builder.Build();
	}
}

少了这部分代码,程序运行时会报错。这里比Xamarin.Forms繁琐。

.NET MAUI疑似Bug

移植过程中发现了两个问题。

  • iOS的AVCaptureDevice.DevicesWithMediaType返回值是null。摄像头获取的代码改用AVCaptureDevice[] videoDevices = AVCaptureDevice.Devices;。再从中选择后置摄像头。

  • Label中的文本显示不全,似乎受到默认text字符串长度影响。如果默认字符串长度小于结果长度,结果会被切割。所以临时的解决方案就是在text中设置很长的空格。

    	<Label FontSize="18"
                FontAttributes="Bold"
                x:Name="ResultLabel"
                   Text="                                        "
                   TextColor="Red"
                HorizontalOptions="Center"
               VerticalOptions="Center"/>
    

程序演示

请添加图片描述

源码

https://github.com/yushulx/maui-barcode-qrcode-scanner

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:00:48  更:2022-07-20 19:01:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:35:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码