本篇将包含以下内容:
1.使用 Visual Studio 2019 开发一个 Android 的 App
2.导入前篇 C++ 编译好的 so 动态库
3.一些入门必须的其它设置
作为入门,我们直接使用真机进行调试,一方面运行速度远高于模拟器,另一方面模拟器使用的 CPU 跟开发主机是一样的 x86,而前篇 C++ 编译的 .so 动态库是 ARM RISC CPU,所以……
作为公益技术博客,我暂时没有开启打赏,不过如果有很多小朋友疯狂点赞,威逼利诱可以追加一篇相关的杂七杂八的设置和基础知识解说
开始吧
新建一个项目,过滤“C#”,“所有平台”,“移动”,选择“移动应用(Xamarin Forms)”
如果从 Android Studio 转过来,或者打算转向 Android Studio 可以选择上图第二项
应用名写 Transformer(不是变形金刚)
为了简单起见,直接选择空白应用,理论上 Xamarin 可以让代码在安卓、苹果、Windows 上运行(至少他们是这么吹的),你可以全部勾选,对于本例我们实际只用 Android
点击创建,实际会生成 4 个项目,分别是 Android,iOS,Windows 的应用项目,还有一个公用的 DLL 核心,前面3个项目是特定平台的应用,但实际的界面和业务逻辑(功能)在 DLL 项目
现在开始折腾UI界面,双击打开 Transformer 项目的 MainPage.xaml 文件,直接在自动生成的控件后面加一个按钮,直接使用自动生成 Click 事件
关于 MVVM 设计模式以及 xaml 语法什么的,以后得空随缘更新吧
再添加一个 Image 用于显示透视裁切的效果,设定 Name 为 image1
展开 MainPage.xaml 树,双击打开 MainPage.xaml.cs 代码文件
声明一个接口,编写 Click 事件代码
虽然我一贯反对复制粘贴,不过时代这种东西……
private async void Button_Clicked(object sender, EventArgs e)
{
var status = await Xamarin.Essentials.Permissions.CheckStatusAsync<Xamarin.Essentials.Permissions.StorageRead>();
if (status != Xamarin.Essentials.PermissionStatus.Granted)
{
status = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageRead>();
}
if (status == Xamarin.Essentials.PermissionStatus.Granted)
{
status = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageWrite>();
}
if (status == Xamarin.Essentials.PermissionStatus.Granted)
{
IStorage storage = DependencyService.Get<IStorage>();
var file = storage.Demo();
this.image1.Source = ImageSource.FromFile(file);
}
}
双击打开 Transformer.Android 项目的 MainActivity.cs 文件
在 Android 项目下实现上面所说的接口,iOS 也另外实现,Windows 同理,但是都可以通过公用的 DLL 项目调用,这就是接口的本质
可以复制粘贴的代码
public class StorageService : Transformer.IStorage
{
[System.Runtime.InteropServices.DllImport("libPerspectiveTransformer.so")]
public static extern void TransformFile(string src, IntPtr src_xys, string dst, IntPtr dst_size);
public string Demo()
{
var downloads = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
string root = downloads.AbsolutePath;
var src = System.IO.Path.Combine(root, "card.jpg");
var dst = System.IO.Path.Combine(root, "card_trans.jpg");
TransformFile(src, IntPtr.Zero, dst, IntPtr.Zero);
return dst;
}
}
一行也算哎
Xamarin.Forms.DependencyService.Register<Transformer.IStorage, StorageService>();
至于这些代码都是什么意思,以后得空随缘更新吧
你可以网上搜,当然如果你求助 AI 国内的大模型跟 ChatGPT 是没法比的,经常给出错误的回答,不过也可以给出一些启发什么的(为什么这些人那么有钱,不让我拿去搞呢)
接下来是关键,引入前面 C++ 编译的 so 动态库
添加一个文件夹
再添加一个 armeabi-v7a 下一级文件夹
关于这个具体是什么意思,你可以网上搜,但是不要问女朋友
你可以简单理解为针对不同的 CPU 类型,暂时不支持 x86、x64(AMD64,x86_64)、ARM64 以及古老的 armeabi 即 64 位最新的手机兼容运行,老旧的手机不能运行,然后双系统平板和电脑端模拟器不能运行(2011年以后大部分的安卓手机和平板都可以运行)
然后,导入 so 动态库文件
选择上一篇 C++ 编译好的 so 文件
属性设置:复制到输出目录=始终复制;生成操作=AndroidNativeLibrary
这一步非常关键,即便你跑去国外查,也很难查得到
很多人是打包成Nuget的方式再引入的,坦白说欧美的教育是重启发、轻知识,1+1=2导师都要你自己去思考,而不直接给你答案
然后我们就可以编译、在手机上调试了
首先,把 Transformer.Android 设为启动项目
项目右键属性,修改以下名称(也可以保持默认)
然后是权限,勾选外部存储读写权限
权限是必须的,在 Android 5.0 (Lollipop,棒棒糖) 以后是动态获取权限(为什么是棒棒糖?)
关于权限,安卓包的结构,异步,各种杂七杂八的,以后得空随缘更新吧
反正示例的代码已经包含有最精简的基本操作了
使用 USB 连接手机,本次调试用的是较旧的二手小米 Redmi 6A 手机,小米手机开启开发者模式需要登陆,注册是免费的,关于手机怎么开启开发者模式,你可以网上搜,大把多
将 USB 连接方式设置为“传输文件”,把示例的图片card.jpg复制到手机的这个文件夹
如果不想翻前一篇,图片是这样的(来源于网络),你可以用自己的图片,不过要自己修改坐标点
工具栏直接点 启动
在手机上点我们设计的“透视裁切”按钮,会弹出请求权限,同意后就显示转正效果
大功告成!