Xamarin.Forms实现扫码登陆程序移动端(上)

本文最后更新于:2022年10月18日 下午

最近学习了 WPF 和 C#进行桌面开发,觉得体验不错,看到 C#+XAML 还可以写移动端跨平台开发,就看了一下官方文档,开发体验比用QMLAndroid 开发好多了。

0.选择 Xamarin.Forms

最近学习了 WPF 和 C#进行桌面开发,觉得体验不错,看到 C#+XAML 还可以写移动端跨平台开发,就看了一下官方文档,开发体验比用QMLAndroid 开发好多了。

赶上@shiluo 兄弟要写扫码登录程序,需要移动端和 Web 前端,于是得到了这个开发练手的机会。

什么是 Xamarin.Forms

官网的描述为:

Xamarin.Forms 是一个开放源代码 UI 框架。 通过 Xamarin.Forms,开发人员可从单个共享基本代码生成 Xamarin.Android、Xamarin.iOS 和 Windows 应用程序。

Xamarin.Forms 使开发人员可以在 C# 中通过代码隐藏在 XAML 中创建用户界面。 这些界面在每个平台上呈现为高性能本机控件。Xamarin.Forms 体系结构图

简单说就是整合了 Xamarin.Android、Xamarin.iOS 和 UWP,使用一套同一的代码生成多端 app。

可以使用 XAML 进行界面设计,用 C#进行业务逻辑开发。

1.总体设计

要实现一个手机扫描网页上的二维码,移动端确认后批准网页端准许登录账户。

移动端登录账户后保存服务端生成的token和账户信息,扫描二维码后确认登录,将二维码提供的uuid和本地token一同发送到服务器,服务器确认后完成登录。

image-20201021180239206
image-20201021180615944

2.创建项目

使用 Visual Studio,创建 Xamarin.Forms 项目。

image-20201021180946177

选择空白模板

image-20201021181452091

3. XAML 界面设计

XAML+C#在 WPF 中的开发体验很好,比 QML+C++强上很多,还有微软在 Windows 上的原生支持,不用 Qt 那一大堆依赖。

不过实际体验下来在 Xamarin 上,虽然语法都大同小异,但是相比 WPF 少了相当多控件。这导致如果想实现一些高级效果要么使用 Xamarin.Android/Xamarin.IOS 开发,要么就只能求助于 NuGet 上的第三方包了。这点看来 QML 实现的功能还是很全面的,但就是开发体验差了 VS 一截,VS 永远的神

建好工程后解决方案资源管理器里会出现一个共享项目工程和你选择要适配平台的对应工程。

我这里只选择了 Android 就只有一个项目名.Android 工程,一些因系统而异的代码将在这里写,其他的通用代码会从共享工程里编译成各平台的代码。

打开共享项目中的 MainPage.xaml 就能看到主界面的前台代码,还可以选择打开设计器辅助开发,F7键可以切换到后台 C#代码。

一共设计四个界面:主界面、扫码界面、确认界面、登陆界面。

主界面

image-20201021183905888

主界面上面显示当前登录的用户(图中是占位示例),下面是一个列表显示操作记录。

在 Xamarin.Forms 中显示图片可以导入到资源、嵌入的资源或使用 URI,这里使用了比较简单的导入 AndroidResource 的方式。将图片放置在项目名.Android 工程中的 Resource/drawable 文件夹中,在下面的生成操作务必设置为 AndroidResource。

右侧的扫码按钮我最开始是设置 Button 的ImageSource属性来显示扫码图标,发现效果不理想,然后发现控件库竟然提供了 ImageButton 控件,效果比我费劲搞出来的按钮好看多了,论看文档的重要性。

image-20201021190126845

扫码界面

在共享项目中添加新项,选择内容页

image-20201021192136625

其中scanViewZXing扫码器预留。这里注意要把这个区域放在上面,否则会被其他内容遮挡。

ContentPage标签中的Title属性将在导航栏显示。

本来想使用Rectangle控件来制作下面的部分,但是会在运行时出现异常,看文档写着图形类的控件还在实验测试中,于是选择使用常规的布局器填充背景颜色来当作矩形使用。

image-20201021203742696

确认界面

头像、用户名和用户类型是占位的,对接后端后将进行绑定。

image-20201022123959946

登录界面

整体结构是一个StackLayout,但是想要显示在整个界面上的加载动画ActivityIndicator并屏蔽用户输入。Xamarin.Forms 提供的弹出窗口只有固定的几种,没法满足这种需求。要想自定义弹出窗口就需要写 Xamarin.Android 或者使用第三方包。

最后的解决方法就是把整个栈式布局外面包一层Grid布局,在上面添加一个默认为禁用的占满全屏的覆盖层,将InputTransparent属性设置为False可以把用户输入拦截在这个透明层,再居中放置一个加载动画,由 C#来控制覆盖层的显示。

image-20201022124833781

4.C#后台设计

储存信息

最重要的信息就是用户登录后的后端发过来的token,官方文档说:“SecureStorage 类有助于安全地存储简单的键/值对。”所有我选择把它存储在SecureStorage中。

按照官方文档操作:

将给定密钥的值保存在安全存储中:

1
2
3
4
5
6
7
8
try
{
await SecureStorage.SetAsync("oauth_token", "secret-oauth-token-value");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}

从安全存储中检索值:

1
2
3
4
5
6
7
8
try
{
var oauthToken = await SecureStorage.GetAsync("oauth_token");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}

删除特定密钥:

1
SecureStorage.Remove("oauth_token");

删除所有密钥:

1
SecureStorage.RemoveAll();

在 App.xaml.cs 中找到App()构造函数,其中 MainPage 为应用的主界面,默认是将主页赋值给 MainPage。因为这里要使用多级页面还需要判断是否登录过来选择页面,所以创建一个NavigationPage导航作为 MainPage。

我将操作封装到了GetMainPage函数中。首先判断能否获取到user_token,如果 token 的Resultnull则将主页设置为登录页,否则设置为主页,如果捕捉到异常则跳转至报错页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static Page GetMainPage()
{
NavigationPage nav;
try
{
var oauthToken = SecureStorage.GetAsync("user_token");
if (oauthToken.Result != null)
{
nav = new NavigationPage(new MainPage());
}
else
{
nav = new NavigationPage(new LoginPage());
}

}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
nav = new NavigationPage(new ErrorPage(ex.Message));
}
return nav;
}

小节

最近比较忙,博客写的有些仓促,还有一些坑没有写出来。虽然写了几年代码,但是一直没有机会写写博客,本人文笔也不好,可能有些地方读起来有些怪,请见谅。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!