技术速递|使用 Native Library Interop 为 .NET MAUI 创建绑定
作者:Rachel Kang
排版:Alan Wang
在当今的应用开发领域,通过利用本机功能来扩展 .NET 应用程序的能力非常宝贵。.NET MAUI 处理程序架构使开发人员能够使用 .NET 代码直接操作本机控件,甚至允许无缝创建跨平台自定义控件。然而,其潜力不仅限于原生平台 API。如果您还能利用本机库 API,将会解锁更多可能性。
适用于 .NET MAUI 的 Native Library Interop(以前称为 Slim Binding 方法)是将本机库集成到 .NET MAUI 应用程序(包括 .NET for Android、.NET for iOS 和 .NET for Mac Catalyst)的替代方法。这种方法能够以既精简又易于维护的方式直接访问本机库 API,从而无需通过传统方法绑定整个库。
您可能会问自己,什么是绑定?当您想要使用不是用 C# 编写的第三方 iOS 或 Android 库时,您需要一种在 .NET MAUI 应用程序中使用它的方法。这就是绑定项目的作用所在,它使您能够创建 C# API 定义来描述本机 API 在 .NET 中的公开方式,以及它如何映射到底层库。建立此定义后,您可以对其进行编译以生成可在 .NET MAUI 应用程序中使用的“绑定”程序集。此过程反映了适用于 iOS 和 Android 的 .NET 的功能;当您在 C# 中使用本机 iOS 或 Android API 时,由于为核心 API 创建的绑定,它是可访问的。
Maui.NativeLibraryInterop 存储库是社区精选示例的宝贵资源,为 .NET 开发人员提供了一个深入研究和受益于共享知识以及贡献自己见解的机会。通过用于创建新绑定的现成模板,它为开发人员从概念到执行的旅程奠定了良好的基础。Native Library Interop 的优点在于它是一种更通用的绑定创建方式,不局限于绑定库,而且从技术上可用于更深入地挖掘原生平台 SDK。
在这篇文章中,我将分享我自己使用 .NET MAUI 的 Native Library Interop 的经验,并提供了一个实际示例来说明如何在 .NET MAUI 应用程序中使用这种创新的方法。请跟随我的步伐,使用模板并遵循入门文档的指导,来实现一个绑定。
开始使用 Native Library Interop 模板
首先,我克隆了 Maui.NativeLibraryInterop 存储库。如果想从现有的绑定示例(Facebook、Firebase、GoogleCast)开始构建,则应从相应文件夹中包含的示例开始。然而,由于我有兴趣从一个完全不同的库创建绑定,因此我将从模板开始!该模板包含使用 Native Library Interop 创建 Android 绑定、iOS 和 Mac Catalyst 绑定以及使用两者的 .NET MAUI 示例应用程序的基础。
获取先决条件
在继续操作之前,请确保您已安装所有先决条件。如果您是长期的 .NET MAUI 开发人员,那么您可能已经像我一样安装了大部分(如果不是全部)先决条件,但请务必检查先决条件的完整列表。
我将绑定什么?
那么我要绑定什么呢?好吧,我想在我的应用程序中包含一个漂亮的饼图!然而 .NET MAUI SDK 目前还没有内置的控件。
虽然我可以使用图表库创建的所有图表都非常漂亮,但我选择了 Native Library Interop 方法,因为我现在只需要在 .NET MAUI 应用程序中使用饼图,所以我只想绑定饼图的 API,仅此而已。
为了创建图表绑定,我将使用适用于 Android 的 MPAndroidChart 库以及适用于 iOS 和 Mac Catalyst 的等效图表库。
因此,我希望绑定名称能够反映这一点。对于 Android,我重命名了 android/native/newbinding/src/main/java/com/example/newbinding/DotnetNewBinding.java 中的类、文件名和 DotnetNewBinding 的所有引用。对于 MaciOS,我对 macios/native/NewBinding/NewBinding/DotnetNewBinding.swift 执行了同样的操作。虽然这是可选的,但我还是决定将项目中的所有文件夹、文件和“newBinding”实例重命名为“charts”。
很好,很简单。接下来是什么?
设置 .NET 绑定库
我计划为 Android、iOS 和 Mac Catalyst 绑定库,我很幸运能够使用我找到的库来支持这三个平台!如果我对所有平台都不感兴趣,我只需删除我不感兴趣平台的文件夹、目标框架和引用即可。
至于 .NET 版本,我目前会继续使用 .NET 8。不过,当我准备使用 .NET 9 时,我会分别更新 Charts.MaciOS.Binding.csproj 和 Charts.Android.Binding.csproj 中的 TargetFrameworks 和版本。
就是这样!虽然我可以选择在这里进行自定义,但除了模板已经为我设置的内容之外,我不需要采取任何额外的步骤来设置 .NET 绑定库。
设置本机包装器项目和库
现在,让我们确保相同的内容反映在本机项目中,并引入本机库!
iOS & Mac Catalyst
首先,我在 Xcode 中打开本机项目 macios/native/Charts/Charts.xcodeproj。我在 Targets > General 中检查支持的目标和 iOS 版本是否符合我的需求,这里我已经准备好了。
现在,是时候引入本机 Charts 库了!由于引入本机库有多种选择,因此此步骤将根据最适合特定库和个人偏好的方式而有所不同。在我的例子中,我将选择使用 Swift 包管理器,方法是导航至 File > Add Package Dependencies…
搜索图表库包,
然后单击添加包。图表库已添加到我的本机 Xcode 项目中!
Android
现在,是时候在 Android 领域做同样的事情了!首先,我在 Android Studio 中打开本机项目 android/native。项目加载后,我打开 build.gradle.kts (:charts) 并确认 compileSdk 版本反映了我的需求。
现在,为了引入本机图表库,我在 build.gradle.kts 中进行了以下编辑:
dependencies {// 添加绑定库的包依赖implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")// 复制绑定库的依赖项"copyDependencies"("com.github.PhilJay:MPAndroidChart:v3.1.0")
}
我还在 settings.gradle.kts 中添加了相关的 maven 存储库:
dependencyResolutionManagement {...repositories {...// 在此处添加存储库maven { url = uri("https://jitpack.io") }}
}
最后但同样重要的一点是,我点击右上角的“Sync Project with Gradle Files”按钮,让可爱的 Gradle 大象开心。
创建 API 接口
现在我们已经引入了本机库,是时候构建我们将在 .NET 应用程序中使用的 API 了!Native Library Interop 方法的妙处在于所有这些都发生在本机端。这意味着我们可以利用库提供的任何现有文档直接用本机语言编写 - 适用于 iOS 和 Mac Catalyst 的 Swift / Objective-C,以及适用于 Android 的 Java / Kotlin。这也意味着我们可以更轻松地更新这些 API,而无需手动将所有内容翻译成 .NET 术语所带来的额外负担。
iOS & Mac Catalyst
在 DotnetCharts.swift 中,我定义了所有我想要的 API。虽然这实际上意味着我可以在 Swift 中定义任何 API,但正如模板字符串示例所示,我现在将专注于创建图表 API 接口的任务,并将在文件顶部导入 DGCharts。
然后,我编写了创建饼图的 API 定义。作为一名 .NET 开发人员,我不能说我是 Swift 方面的专家,但可以直接从 Charts 库存储库中利用 Swift 示例,并获得 GitHub Copilot 的帮助,这无疑是一个改变游戏规则的举措,使这一部分变得不再那么令人生畏。
一旦我通过成功构建 Xcode 项目来确保我的 Swift 代码有效后,我就会尽快回到 .NET 这边,以确保本机库确实可以互操作。
我从 macios/Charts.MaciOS.Binding 运行 dotnet build。这会在 macios/native/Charts/bin/Release/net8.0-ios/sharpie/Charts/ApiDefinitions.cs 中生成 .NET API 定义,然后我将其复制到 charts/macios/Charts.MaciOS.Binding/ApiDefinition.cs 中。
然后,我再次运行 dotnet build 以确保一切正常。🙂
Android
现在又回到 Android 世界了!在 DotnetCharts.java 中,我可以用 Java 定义任何 API,正如这里的模板字符串示例所示。不过,为了专注于图表,我将导入我需要的所有内容。虽然这些库非常相似,但它们的实现略有不同,这也会影响我在此处导入和定义 API 的方式。
因此,我从 com.github.mikephil.charting 导入以下内容:
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.github.mikephil.charting.utils.ColorTemplate;
然后,我再次编写了创建饼图的 API 定义。正如我不是最精通 Swift 的专家一样,我也不是最精通 Android 的专家……但我仍然是一名移动应用开发人员!能够直接利用在线资源和 GitHub Copilot 使这一切变得非常可行。🙂
再次回到舒适的 .NET,我导航到 android/Charts.Android.Binding 并运行 dotnet build。
这将在 android/native/charts/bin/Release/net8.0-android/outputs/deps/MPAndroidChart-v3.1.0.aar 中生成依赖项的副本,与 iOS 和 Mac Catalyst 不同,我需要在我的 .NET 示例应用程序中直接引用它,方法是将以下内容添加到 MauiSample.csproj:
<!-- Reference the Android binding dependencies -->
<ItemGroup Condition="$(TargetFramework.Contains('android'))"><AndroidLibrary Include="..\android\native\charts\bin\Release\net8.0-android\outputs\deps\MPAndroidChart-v3.1.0.aar"><Bind>false</Bind><Visible>false</Visible></AndroidLibrary>
</ItemGroup>
在 .NET 应用中使用 API
现在是关键时刻! 图表绑定现在可以用于任何新的或现有的 .NET MAUI 应用程序,包括任何 .NET for iOS、.NET for Mac Catalyst 和 .NET for Android 应用程序。为了简单起见,我将在模板附带的 .NET MAUI 示例应用程序中使用它,该示例应用程序已在 MauiSample.csproj 中为我引用了 .NET 绑定库:
<!-- 参考 MaciOS Binding 项目 -->
<ItemGroup Condition="$(TargetFramework.Contains('ios')) Or $(TargetFramework.Contains('maccatalyst'))"><ProjectReference Include="..\macios\Charts.MaciOS.Binding\Charts.MaciOS.Binding.csproj" />
</ItemGroup>
<!-- 参考Android Binding项目 -->
<ItemGroup Condition="$(TargetFramework.Contains('android'))"><ProjectReference Include="..\android\Charts.Android.Binding\Charts.Android.Binding.csproj" />
</ItemGroup>
在 MainPage.xaml.cs 中,我导入 ChartsMaciOS.DotnetCharts 和 ChartsAndroid.DotnetCharts,并使用平台指令来直接利用我创建的 API,就像我在 .NET MAUI 中使用任何其他特定于平台的实现一样。
public class MauiPieChart : View
{public List<PieChartSlice> Slices { get; set; } = new List<PieChartSlice>();
}
public class PieChartSlice
{public string Name { get; set; } = string.Empty;public int Count { get; set; }public Color Color{get => _color ??= GenerateRandomColor();set => _color = value;}private Color? _color = null;private Color GenerateRandomColor(){Random random = new Random();return new Color(random.Next(256), random.Next(256), random.Next(256));}
}
public partial class MauiPieChartHandler
{public static IPropertyMapper<MauiPieChart, MauiPieChartHandler> PropertyMapper = new PropertyMapper<MauiPieChart, MauiPieChartHandler>(ViewHandler.ViewMapper){};public MauiPieChartHandler() : base(PropertyMapper){}
}
#if IOS || MACCATALYST
public partial class MauiPieChartHandler : ViewHandler<MauiPieChart, UIKit.UIView>
{protected override UIKit.UIView CreatePlatformView(){ var data = Foundation.NSDictionary<Foundation.NSString, Foundation.NSNumber>.FromObjectsAndKeys (VirtualView.Slices.Select(s => new Foundation.NSNumber(s.Count)).ToArray(),VirtualView.Slices.Select(s => s.Name).ToArray());var colors = VirtualView.Slices.Select(s => s.Color.ToPlatform()).ToArray();var pieChart = Charts.CreatePieChartWithData(data, colors);return pieChart;}
}
#elif ANDROID
public partial class MauiPieChartHandler : ViewHandler<MauiPieChart, Android.Views.View>
{protected override Android.Views.View CreatePlatformView(){var data = new Java.Util.LinkedHashMap();var colors = new List<Java.Lang.Integer>();foreach (var slice in VirtualView.Slices) {data.Put(slice.Name, slice.Count);colors.Add(new Java.Lang.Integer(slice.Color.ToPlatform().ToArgb()));}var pieChart = Charts.CreatePieChart(Microsoft.Maui.ApplicationModel.Platform.CurrentActivity, data, colors);return pieChart;}
}
#endif
现在,我可以从我的用户界面访问这个新的 MauiPieChart:
<local:MauiPieChart WidthRequest="300" HeightRequest="300"><local:MauiPieChart.Slices><local:PieChartSlice Name="Dave's fans" Count="1" /><local:PieChartSlice Name="Rachel's fans" Count="5" /><local:PieChartSlice Name="Maddy's fans" Count="7" /><local:PieChartSlice Name="Beth's fans" Count="10" /></local:MauiPieChart.Slices>
</local:MauiPieChart>
瞧!我向您展示了 .NET MAUI 中漂亮的饼图!
您要绑定什么?
感谢你跟随我创建图表绑定的 Native Library Interop 之旅!若要查看所有代码,包括我的 API 定义和示例用法的详细信息,您可以在 https://github.com/rachelkang/MauiCharts 找到完整示例。若要了解有关 Native Library Interop 方法的更多信息,发现简化这一过程的内在魔力,并更好地了解何时使用它,请务必查看我们的文档。
我希望看到我的过程能够给您带来一些令人兴奋的想法,让您了解使用 Native Library Interop 的无限可能性!
请务必亲自查看 CommunityToolkit/Maui.NativeLibraryInterop。我很想看看您创建了哪些绑定,并听听您的使用体验!
相关文章:

技术速递|使用 Native Library Interop 为 .NET MAUI 创建绑定
作者:Rachel Kang 排版:Alan Wang 在当今的应用开发领域,通过利用本机功能来扩展 .NET 应用程序的能力非常宝贵。.NET MAUI 处理程序架构使开发人员能够使用 .NET 代码直接操作本机控件,甚至允许无缝创建跨平台自定义控件。然而&a…...

Linux笔记 --- 标准IO
系统IO的最大特点一个是更具通用性,不管是普通文件、管道文件、设备节点文件、接字文件等等都可以使用,另一个是他的简约性,对文件内数据的读写在任何情况下都是带任何格式的,而且数据的读写也都没有经过任何缓冲处理,…...

洛谷:B3625 迷宫寻路
迷宫寻路 题目描述 机器猫被困在一个矩形迷宫里。 迷宫可以视为一个 n m n\times m nm 矩阵,每个位置要么是空地,要么是墙。机器猫只能从一个空地走到其上、下、左、右的空地。 机器猫初始时位于 ( 1 , 1 ) (1, 1) (1,1) 的位置,问能否…...

【C#】explicit、implicit与operator
字面解释 explicit:清楚明白的;易于理解的;(说话)清晰的,明确的;直言的;坦率的;直截了当的;不隐晦的;不含糊的。 implicit:含蓄的;不直接言明的;成为一部分的;内含的;完全的;无疑问的。 operator:操作人员;技工;电话员;接线员;…...

Vue:Vuex-Store使用指南
一、简介 1.1Vuex 是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window)…...

对经典动态规划问题【爬台阶】的一些思考
背景 今天在做Leetcode题目时,做到了一道经典的动态规划问题:爬楼梯,题目的大致意思很简单,有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上…...

开发一个能打造虚拟带货直播间的工具!
在当今数字化时代,直播带货已成为电商领域的一股强劲力量,其直观、互动性强的特点极大地提升了消费者的购物体验。 然而,随着技术的不断进步,传统直播带货模式正逐步向更加智能化、虚拟化的方向演进,本文将深入探讨如…...

汽车补光照明实验太阳光模拟器光源
汽车补光照明实验概览 汽车补光照明实验是汽车照明领域的一个重要环节,它涉及到汽车照明系统的性能测试和优化。实验的目的在于确保汽车在各种光照条件下都能提供良好的照明效果,以提高行车安全。实验内容通常包括但不限于灯光的亮度、色温、均匀性、响应…...

MediaPipe人体姿态、手指关键点检测
MediaPipe人体姿态、手指关键点检测 文章目录 MediaPipe人体姿态、手指关键点检测前言一、手指关键点检测二、姿态检测三、3D物体案例检测案例 前言 Mediapipe是google的一个开源项目,用于构建机器学习管道。 提供了16个预训练模型的案例:人脸检测、…...

树上dp之换根dp
基本概念: 换根dp是树上dp的一种 我们在什么时候需要用到换根dp呢? 当题目询问的属性,是需要当前结点为根时的属性,这个时候,我们就要使用换根dp 换根dp的基本思路: 假设题目询问的的属性为x 通常我们…...

2024/8/13 英语每日一段
Mackey says while Whole Foods has become more homogenized under Amazon, it did enable the store to do what it couldn’t have done independently. “People saw us as too expensive and out of touch with our customers,” he says. “The main thing Whole Foods n…...

Java多线程练习(1)
MultiProcessingExercise package MultiProcessingExercise120240813;public class MultiProcessingExercise {public static void main(String[] args) {/*需求:一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,请用多线程模拟卖票过程并打印…...

AI高级肖像动画神器LivePortrait
文章目录 前言一、安装1.1 源码安装1.2 windows一键启动包 二、人像生成2.1 浏览器2.2 输入图像2.3 选择驱动视频2.4 生成2.5 结果 三、动物生成3.1 浏览器3.2 输入图片3.3 选择视频3.4 生成3.5 最终结果 四、软件获取 前言 最近,快手可灵大模型团队、中国科学技术…...

Java反射机制深度解析与实践应用
Java反射机制深度解析与实践应用 引言 Java反射是Java语言提供的一种能力,允许程序在运行时访问、检测和修改其自身的属性和行为。反射机制是Java面向对象编程的一大亮点,也是Java框架和库常用的技术之一。 反射的基本概念 反射的核心是java.lang.re…...

Oracle递归查询层级及路径
一、建表及插入数据 ocation_idlocation_nameparent_location_id1广东省NULL2广州市13深圳市14天河区25番禺区26南山区37宝安区3 建表sql: CREATE TABLE locations (location_id NUMBER PRIMARY KEY,location_name VARCHAR2(100),parent_location_id NUMBER ); I…...

leetcode300. 最长递增子序列,动态规划附状态转移方程
leetcode300. 最长递增子序列 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2…...

C语言:字符串函数strcpy
该函数用于字符串的拷贝。 使用方法如下: #include<stdio.h> #include<string.h>int main() {char str[10];char* str1 "abcd";//strcpy(str, str1);//把str1复制到str,但此函数不安全所以用strcpy_sstrcpy_s(str, 10, str1);/…...

Day16-指针2
数组指针与指针数组 变量指针:指向变量的地址。 数组指针:指向数组的地址。 指针变量:存放其他变量地址的变量。 指针数组:存放数组元素指针的变量。 数组指针 概念:数组指针是指向数组的指针。特点: 先…...

数据结构(5.5_3)——并查集的进一步优化
Find操作的优化(压缩路径) 压缩路径——Find操作,先找到根节点,再将查找路径上所有结点都挂到根结点下 代码: //Find "查"操作优化,先找到根节点,再进行"路径压缩" int Find(int S[], int x) {…...

(回溯) LeetCode 131. 分割回文串
原题链接 一. 题目描述 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串。返回 s 所有可能的分割方案。 示例 1: 输入:s "aab" 输出:[["a","a","b"],[…...

【Linux】阻塞信号|信号原理|深入理解捕获信号|内核态|用户态|sigaction|可重入函数|volatile|SIGCHILD|万字详解
目录 编辑 一,常见的信号术语 二,信号在内核中的表示 信号标志位 Pending表 Block表 handler表 POSIX.1标准 三,sigset_t 信号集操作函数 sigemptyset sigfillset sigaddset sigdelset sigismember sigprocmask sig…...

基于Linux对 【进程地址空间】的详细讲解
研究背景: ● kernel 2.6.32 ● 32位平台 –❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀– 在学习操作系统中想必大家肯定都见过下面这…...

[python]使用Pandas处理多个Excel文件并汇总数据
在数据分析和处理过程中,经常需要处理多个Excel文件,并将其中的数据进行汇总和分析。本文介绍使用Python的Pandas库来读取多个Excel文件,并汇总不同类型的数据,例如员工工资、工件数量等。 代码示例 以下是一个完整的代码示例&a…...

提升体验:UI设计的可用性原则
在中国,每年都有数十万设计专业毕业生涌入市场,但只有少数能够进入顶尖企业。尽管如此,所有设计师都怀揣着成长和提升的愿望。在评价产品的用户体验时,我们可能会依赖直觉来决定设计方案,或者在寻找改善产品体验的切入…...

x264 编码器 SSIM 算法源码分析
SSIM SSIM(Structural Similarity Index)是一种用于衡量两幅图像之间视觉相似度的指标。它不仅考虑了图像的亮度、对比度和饱和度,还考虑了图像的结构信息。SSIM的值介于-1到1之间,值越接近1表示两幅图像越相似。 SSIM是基于以下三个方面来计算的: 亮度(Luminance):比…...

echarts使图表组件根据屏幕尺寸变更而重新渲染大小
效果图: 通过 window.addEventListener(resize, this.resizeChart); 实现 完整代码: <template><div class"dunBlock"><div class"char2" id"char2" ref"chart"></div></div…...

电脑图片损坏打不开怎么办?能修复吗?
照片和视频是记录和保存现实生活中的事件的最好方式。由于手机储存空间有限,一般我们会把有纪念意义的照片放到电脑上进行保存,但有时难免会遇到照片被损坏打不开的情况,一旦遇到这种情况,先不要急,也不要因为照片打不…...

vue-cli(二)
箭头函数 一般的函数: 这里window是用来调用函数的 function fun(){console.log(this) } window.fun(); 箭头函数: 1、如果只有一个参数,形参的小括号可以省略 2、如果只有一条语句,{}可以省略 完整的写法 let fun2 a>…...

今日头条的账号id在哪里看(网页版)
今日头条的账号id在哪里看(网页版) 1.https://mp.toutiao.com/profile_v4/index2.登录今日头条账号3.设置->头条号ID 1.https://mp.toutiao.com/profile_v4/index 2.登录今日头条账号 3.设置->头条号ID 打开下方链接: https://mp.to…...

单体应用提高性能和高并发处理-合理使用多核处理
合理使用多核处理能力是提升单体应用性能和处理高并发能力的重要手段。以下是关于如何合理利用多核处理器的详细讲解,包括多线程编程、线程池的使用、并行计算、以及如何避免常见的性能陷阱。 1. 多线程编程 多线程编程是利用多核处理器的直接方式。每个线程可以在…...