Xamarin.Forms实现扫码登陆程序移动端(上)
0.选择Xamarin.Forms
最近学习了WPF和C#进行桌面开发,觉得体验不错,看到C#+XAML还可以写移动端跨平台开发,就看了一下官方文档,开发体验比用QML
Android开发好多了。
赶上@shiluo兄弟要写扫码登录程序,需要移动端和Web前端,于是得到了这个开发练手的机会。
什么是Xamarin.Forms
官网的描述为:
Xamarin.Forms 是一个开放源代码 UI 框架。 通过 Xamarin.Forms,开发人员可从单个共享基本代码生成 Xamarin.Android、Xamarin.iOS 和 Windows 应用程序。
Xamarin.Forms 使开发人员可以在 C# 中通过代码隐藏在 XAML 中创建用户界面。 这些界面在每个平台上呈现为高性能本机控件。
简单说就是整合了Xamarin.Android、Xamarin.iOS和UWP,使用一套同一的代码生成多端app。
可以使用XAML进行界面设计,用C#进行业务逻辑开发。
1.总体设计
要实现一个手机扫描网页上的二维码,移动端确认后批准网页端准许登录账户。
移动端登录账户后保存服务端生成的token
和账户信息,扫描二维码后确认登录,将二维码提供的uuid
和本地token
一同发送到服务器,服务器确认后完成登录。
2.创建项目
使用Visual Studio,创建Xamarin.Forms项目。
选择空白模板
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#代码。
一共设计四个界面:主界面、扫码界面、确认界面、登陆界面。
主界面
主界面上面显示当前登录的用户(图中是占位示例),下面是一个列表显示操作记录。
在Xamarin.Forms中显示图片可以导入到资源、嵌入的资源或使用URI,这里使用了比较简单的导入AndroidResource的方式。将图片放置在项目名
.Android工程中的Resource/drawable文件夹中,在下面的生成操作务必设置为AndroidResource。
右侧的扫码按钮我最开始是设置Button的ImageSource
属性来显示扫码图标,发现效果不理想,然后发现控件库竟然提供了ImageButton控件,效果比我费劲搞出来的按钮好看多了,论看文档的重要性。
扫码界面
在共享项目中添加新项,选择内容页
其中scanView
为ZXing
扫码器预留。这里注意要把这个区域放在上面,否则会被其他内容遮挡。
ContentPage
标签中的Title
属性将在导航栏显示。
本来想使用Rectangle
控件来制作下面的部分,但是会在运行时出现异常,看文档写着图形类的控件还在实验测试中,于是选择使用常规的布局器填充背景颜色来当作矩形使用。
登录界面
整体结构是一个StackLayout
,但是想要显示在整个界面上的加载动画ActivityIndicator
并屏蔽用户输入。Xamarin.Forms提供的弹出窗口只有固定的几种,没法满足这种需求。要想自定义弹出窗口就需要写Xamarin.Android或者使用第三方包。
最后的解决方法就是把整个栈式布局外面包一层Grid
布局,在上面添加一个默认为禁用的占满全屏的覆盖层,将InputTransparent
属性设置为False
可以把用户输入拦截在这个透明层,再居中放置一个加载动画,由C#来控制覆盖层的显示。
4.C#后台设计
储存信息
最重要的信息就是用户登录后的后端发过来的token
,官方文档说:“SecureStorage 类有助于安全地存储简单的键/值对。”所有我选择把它存储在SecureStorage
中。
按照官方文档操作:
将给定密钥的值保存在安全存储中:
try { await SecureStorage.SetAsync("oauth_token", "secret-oauth-token-value"); } catch (Exception ex) { // Possible that device doesn't support secure storage on device. }
从安全存储中检索值:
try { var oauthToken = await SecureStorage.GetAsync("oauth_token"); } catch (Exception ex) { // Possible that device doesn't support secure storage on device. }
删除特定密钥:
SecureStorage.Remove("oauth_token");
删除所有密钥:
SecureStorage.RemoveAll();
在App.xaml.cs中找到App()
构造函数,其中MainPage为应用的主界面,默认是将主页赋值给MainPage。因为这里要使用多级页面还需要判断是否登录过来选择页面,所以创建一个NavigationPage
导航作为MainPage。
我将操作封装到了GetMainPage
函数中。首先判断能否获取到user_token
,如果token的Result
为null
则将主页设置为登录页,否则设置为主页,如果捕捉到异常则跳转至报错页面。
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;
}
小节
最近比较忙,博客写的有些仓促,还有一些坑没有写出来。虽然写了几年代码,但是一直没有机会写写博客,本人文笔也不好,可能有些地方读起来有些怪,请见谅。