四、使用 Xamarin 开发移动应用

在使用 Xamarin 进行跨平台开发时,重要的是要理解应用源代码不能完全跨平台。 Xamarin 应用的平台无关模块各不相同,这取决于应用的内容以及所使用的开发方法。 Xamarin classic 和 Xamarin。 表单是(主要)用于创建 iOS 和 Android 平台的本地应用的两种不同方法。 虽然 Xamarin 经典使用了更原生的方法,但实际上是将原生平台实现策略迁移到。net 生态系统 Xamarin。 Forms 为本机 UI 实现提供了一个额外的抽象层。

在本章中,我们将学习 Xamarin 和 Xamarin。 制定开发策略并创建 Xamarin。 形式应用,我们将在本书的剩余部分开发。 我们还将讨论与表示层以及其他层相关的架构模型和设计模式。

下面的章节将指导你使用 Xamarin 框架和工具集实现一个跨平台的本地移动应用:

  • 在 Xamarin 和 Xamarin 之间做选择。 形式
  • 组织 Xamarin 的。 表单应用项目
  • 选择表示架构
  • 有用的架构模式

在本章结束时,您将能够设置 Xamarin 的初始结构。 表单应用与表示模型以及主要体系结构。

技术要求

你可以在本书的 GitHub 库中找到本章将要使用的代码:https://github.com/PacktPublishing/Mobile-Development-with-.NET-Second-Edition/tree/master/chapter04

选择 Xamarin 和 Xamarin。 形式

Xamarin 作为运行时和框架,为开发人员提供了创建跨平台应用所需的所有工具。 在这个任务中,一个关键目标是用最少的资源和时间创建一个代码库; 另一个是降低项目的维护成本。 这就是 Xamarin。 形式进入画面。

正如我们之前在第 2 章定义 Xamarin、Mono 和。net Standard中所解释的,通过使用 Xamarin 的经典方法,开发人员可以创建本地应用。 使用这种方法,我们并不真正担心创建跨平台应用的问题,因为我们正在创建一个应用,因为所有目标平台都使用相同的开发工具和语言。 在这种情况下,目标平台之间的共享组件将仅限于业务逻辑(即视图模型)和数据访问层(即模型)。 但是,如果我们处理的是消费者应用,业务逻辑和数据访问层的实现对于移动应用来说就不那么重要了。

在云连接的移动应用中,作为主流面向消费者的套件的一部分,我们通常希望实际的域实现被转移到基于云的服务中。 然后,这些服务将通过针对特定平台的服务 façade 提供。 这样,移动应用只负责通过网关 API façade 执行简单的服务调用,该网关被标记为下游微服务 bundle 上的客户端网关,如下图所示:

Figure 4.1 – Cloud-Connected Client Service Backend

图 4.1 -云连接客户端服务后端

在基础设施的设置类似,其中每个应用(也就是说,多个移动应用,以及 web 应用)-标记为客户在前面的图中可以受益于一个定制的移动 API 网关(即端网关),该平台实现相互偏离, 主要是因为单独的 UI 层。 换句话说,扩展共享业务逻辑和数据访问层并不能真正增加共享代码的数量。

一般的经验法则是,Xamarin 经典应用建议与具有关键特性的应用一起使用,这些特性依赖于它们所运行的平台(外围 api、内在 UI 组件、性能需求等等)。 但是,对于具有云服务后台的主流移动应用,使用 Xamarin.Forms 可能是更好的选择。

Xamarin 的。 表单框架的目标是标准化 UI 实现过程,同时保持应用在多个平台上的原生性。 本质上,就是使用 Xamarin 创建的应用。 表单使用来自目标平台的本地 UI 元素呈现。 事实上,编译和链接之后,Xamarin 应用包与 Xamarin 没有什么不同。 窗体应用的任何给定的目标平台。

简而言之,就是 Xamarin。 表单应用可能更适合面向消费者的、云连接的应用,因为实际的业务关注点和相关领域是在服务层实现的,而移动应用只负责提供对服务层的访问,提供最直观和最吸引人的用户界面。

组织 Xamarin。 表单应用项目

即使是最简单的云连接的移动应用也可能发展到无法管理的规模。 为了控制项目的可维护性,您需要以适当的结构组织应用的不同层。 让我们看看 Xamarin 的不同层。 表单应用以及如何组织这些层以获得更好的可维护性。

开发 Xamarin 时。 表单应用,应用的要领包括目标平台项目。 这些项目也认识 NAS 负责人,它们充当初始化 Xamarin 的工具。 表单框架和应用,它们还包含本地呈现或 API 实现。 此外,我们将拥有一个包含 Xamarin 的平台无关的项目。 形式的观点。 与平台无关的项目还将包含平台抽象,以便自定义组件可以在特定于平台的项目上实现。

随着项目的增长,开发人员将需要创建一个单独的项目,该项目将只包含视图模型和平台无关的服务的实现。 在这种情况下,项目将成为单元测试过程的主要目标,因为该层不直接依赖于 UI 元素或平台服务。 此外,可以使用一个单独的项目在服务层和客户端应用之间共享数据传输对象(DTO)模型。 在这样的设置中,整体架构布局看起来类似如下:

Figure 4.2 – Xamarin.Forms Project Structure

图 4.2 - Xamarin。 项目结构形式

前面的图显示了如何将这些组件分组到单独的项目中,以及它们的依赖结构是什么样子的。 如左侧所示,我们有平台管理项目,其中包含对本机平台的引用,而在右侧,我们有. net 标准实现,它构成了应用中与平台无关的部分。 依赖层次结构中的最高元素是原生项目。 在层次结构的底部,我们有公共包,它包含各种实用程序、数据契约和对应用服务的可能抽象。

在某些需要测试特定于平台的 api 的实现中,使用特定于平台的单元测试,这些测试在目标平台上而不是在开发平台本身上执行。 这些测试也可以是平台集成测试。 此外,您可能会看到自动 UI 测试被添加到移动解决方案中,这些测试将作为功能测试的一部分执行。

在 Xamarin 中考虑这个结构。 表单应用,让我们看看下面的用户故事:

“作为产品所有者,我想为我们的跨店购物平台创造一个移动解决方案,这样目标消费群可以扩展到 iOS 和 Android 的移动用户。”

在这个故事中,正如我们前面提到的,我们有一个基于云的服务后端实现,它已经被一个已建立的 web 应用使用。 产品所有者正在考虑将后端公开给一组移动应用。

让我们通过创建初始解决方案来开始实现这个故事。 遵循以下步骤:

  1. Create an empty solution called ShopAcross.Mobile that will contain the mobile application projects using the Blank Solution project template under the Miscellaneous section:

    Figure 4.3 – Visual Studio – New Solution

    图 4.3 - Visual Studio -新解决方案

  2. 创建一个名为Client的解决方案文件夹。

  3. 创建 Xamarin 的。 使用Blank Forms App创建项目。 应用名称使用ShopAcross,项目名称使用ShopAcross.Mobile.Client
  4. Now, create a .NET Standard Library project that will contain the DTO contacts for service communication called ShopAcross.Service.Data.

    在更大的项目中,这个 DTO 包可能以 NuGet 包的形式交付,但是在这里,让我们假设我们手动创建数据契约。

  5. Next, create the application core project using the .NET Standard Library project template called ShopAcross.Mobile.Core.

    我们将使用这个项目来存储视图模型。 由于在这个项目中,我们可能会使用一些在数据绑定中使用的原语,所以我们还应该添加一个对Xamarin.FormsNuGet 包的引用。

  6. 现在,从ShopAcross.Mobile.Core项目的中,向ShopAcross.Service.Data项目添加一个项目引用。

  7. 将来自ShopAcross.Mobile.ClientShopAcross.Mobile.Client.iOSShopAcross.Mobile.Client.Android项目的其他引用添加到ShopAcross.Mobile.Core中。

最终的结构应该类似如下:

Figure 4.4 – Xamarin.Forms Projects

图 4.4 - Xamarin 表单项目

在本节中,我们了解了组成 Xamarin 的元素。 表单应用。 我们仔细研究了这些元素的功能以及它们是如何相互依赖的。 最后,使用这些关于不同层的信息,我们为示例移动应用创建了初始解决方案结构。

选择表示架构

既然我们已经准备好了项目结构并将应用划分为上下文相关的层,那么我们就应该为应用如何将从数据源检索的数据呈现给用户建立一个结构模型。 让我们仔细看看在使用表示层时可以利用的不同结构模型。

在开发跨平台移动应用时,选择表示架构可能是最关键的决策之一。 视图和业务逻辑实现应该考虑所选模式所需的体系结构概念。

模型-视图-控制器(MVC)实现

iOS 和 Android 平台本质上都被设计为使用模型-视图-控制器(MVC)模式的派生。 如果我们处理本机应用,这将是最符合逻辑的路径使用 MVC调节控制器为 iOS 和【显示】Model-View-Presenter(【病人】MVP)或略派生版本,然后Model-View-Adapter【t16.1】(MVA)模式为 Android。 严格的 MVC 模式以单向数据流为目标,并简化了实现:**

Figure 4.5 – Classic MVC Flow

图 4.5 -经典 MVC 流程

MVC 模式的诞生是对单一责任原则的回应。 在此模式中,View(UI 实现)组件负责显示从模型(服务层)接收到的数据。 在前一个图的中,这个流程用Updates标签进行了注释。 然后,视图负责将用户输入委托给控制器。 从某种意义上说,用户本质上与控制器进行交互(也就是说,他们使用控制器)。 然后控制器通过操作模型来传达用户所要求的更改。

虽然它被广泛用于 web 应用,但通常,它的派生版本用于移动和桌面应用。 这种模式的衍生品包括 MVA 和 MVP。

MVA 架构(或调节控制器),适配器之间充当中介模型,并负责定义策略,一个或多个视图组件,以及充当观察者这些 UI 组件:****

**Figure 4.6 – Mediating Controller

图 4.6 -中介控制器

在 MVC 实现中,当同时使用经典和中介模式时,控制器成为应用的核心和灵魂。 需要意识到模型,以及查看(紧密耦合),因为它实现了事件战略视图(用户输入)作为战略实施者和观察者。

让我们在为上一节中创建的应用实现登录视图的同时演示这个模式:

  1. 首先,我们需要创建视图。 在ShopAcross.Mobile项目中使用名为LoginView的 XAML 设计组件创建内容页面:

    <ContentPage xmlns=http://xamarin.com/schemas/2014/forms xmlns:x=http://schemas.microsoft.com/winfx/2009/xaml x:Class="ShopAcross.Mobile.Client.LoginView"> <ContentPage.Content> <StackLayout VerticalOptions="CenterAndExpand" Padding="20"> <Label Text="Username" /> <Entry x:Name="usernameEntry" Placeholder="username"/> <Label Text="Password" /> <Entry x:Name="passwordEntry" IsPassword="true" Placeholder="password" /> <Button x:Name="loginButton" Text="Login" /> <Label x:Name="messageLabel" /> </StackLayout> </ContentPage.Content> </ContentPage>

  2. 接下来,创建两个条目(即用户名和密码)、一个用于登录的按钮和一个标签字段,我们将使用它来显示登录函数的结果。

  3. 为了支持该视图的控制器实现,该控制器将处理字段验证,以及登录和注册操作,我们必须将入口和按钮组件暴露给关联的控制器:

    ``` public partial class LoginView : ContentPage { // private LoginViewController _controller;

     public LoginView()
     {
         InitializeComponent();
    
         // _controller = new LoginViewController(this);
     }
    
     internal Entry UserName { get { return this.usernameEntry; }}
    
     internal Entry Password { get { return this.passwordEntry; }}
     internal Label Result { get { return this.messageLabel; }}
    
     internal Button Login { get { return this.loginButton; }}
    

    } ```

  4. 现在,使用引用 t 创建控制器到视图类:

    ``` public class LoginViewController { private LoginView _loginView;

     public LoginViewController(LoginView view)
     {
         _loginView = view; 
     }
    

    } ```

  5. 控制器应该为由于用户交互而在视图上引发的各种事件定义事件处理方法。 向控制器类添加以下事件处理程序方法:

    public class LoginViewController { // … cont'd void LoginClicked(object sender, EventArgs e) { // TODO: Login _loginView.Result.Text = "Successfully Logged In!"; } void UserNameTextChanged(object sender, TextChangedEventArgs e) { // TODO: Validate } }

  6. 既然已经创建了事件处理程序方法,我们就可以订阅视图引用上的事件了。 您可以将这些订阅调用添加到构造函数:

    public LoginViewController(LoginView view) { _loginView = view; _loginView.Login.Clicked += LoginClicked; _loginView.UserName.TextChanged += UserNameTextChanged; }

  7. 最后,您可以注释掉LoginView类中的控制器引用。

如您所见,尽管我们可以进一步重构代码,以便在控制器和视图之间插入抽象层,但两者之间存在强耦合。 为了解决这个问题,我们可以使用Model-View-ViewModel(MVVM)设置。

Model-View-ViewModel (MVVM)实现

在对紧密耦合问题的回应中,另一个 MVC 的衍生版本随着Windows Presentation Foundation(WPF)的发布而诞生。 由 Microsoft 提出的想法是由控制器(在本例中为ViewModel)公开出口,出口与视图元素耦合的概念。 这些插座及其耦合的概念被称为 abinding。 在 MVVM 模式中,绑定替换了ViewViewModel之间的双向通信路径,而ViewModel仍然与Model紧密耦合:

Figure 4.7 – MVVM Pattern

图 4.7 - MVVM 模式

使用绑定,我们可以减少视图模型对视图元素内部工作的了解,然后让应用运行时处理如何同步ViewViewModel的出口。

除了绑定之外,“命令”的概念也变得非常重要,因此我们可以将用户操作委托给ViewModel。 命令是一个有状态的单个执行单元,它表示用于执行该函数的函数和数据。

使用前面的例子,我们可以创建一个视图模型来演示使用 MVVM 的好处:

  1. Let's start by creating a class that will represent the user interaction points on our view. Create a new class under the ShopAcross.Mobile.Core project called LoginViewModel:

    ``` public class LoginViewModel { private string _userName;

     private string _password;
     private string _result;
    
     public LoginViewModel()
     {
     }
    

    } ```

    请注意,类定义了三个字段,它们代表视图上的两个输入字段,分别是用户名和密码,以及一个显示登录结果的结果字段。

  2. 接下来,让我们粗略地暴露这些字段的一些属性:

    public class LoginViewModel { // … cont'd public string UserName { get { return _userName; } set { if (_userName != value) { _userName = value; } } } public string Password { get { return _password; } set { if (_password != value) { _password = value; } } } public string Result { get { return _result; } set { if (_result != value) { _result = value; } } } }

  3. 最后添加Login功能:

    public class LoginViewModel { // … cont'd public void Login() { //TODO: Login Result = "Successfully Logged In!"; } }

  4. At this stage of the implementation, we can bind the Entry fields from our view to the view-model. To assign the view-model to the view, we need to use BindingContext from our LoginView, making sure the ShopAcross.Mobile.Core namespace is added with a using statement:

    public partial class LoginView : ContentPage { public LoginView() { InitializeComponent(); //_controller = new LoginViewController(this); BindingContext = new LoginViewModel(); } }

    现在我们不再使用控制器,您还可以删除暴露控件的出口(内部属性)。

  5. Now, let's set up the bindings for the Entry fields:

    <Label Text="Username" /> <Entry x:Name="usernameEntry" Placeholder="username" Text="{Binding UserName}" /> <Label Text="Password" /> <Entry x:Name="passwordEntry" IsPassword="true" Placeholder="password" Text="{Binding Password}" /> <Button x:Name="loginButton" Text="Login" /> <Label x:Name="messageLabel" Text="{Binding Result}" />

    在执行此示例时,您将注意到包含单向数据流(即,UserNamePassword字段仅从View传播到视图模型)的条目的值的行为符合预期; 在关联字段中输入的值将按预期推到属性中。

视图到视图模型绑定上下文的设置也可以在 XAML 中完成。 <ContentPage.BindingContext>可用于将绑定上下文设置到视图模型,该视图模型是使用正确的clr名称空间(例如<core:LoginViewModel />)初始化的。 为了使其像预期的那样工作,视图模型类需要有一个无参数的构造函数。

为了提高绑定的性能并减少用于某个绑定的资源,重要的是要定义绑定的方向。 有多种BindingMode可供选择,如下:

  • 单向:在ViewModel更新值时使用。 它应该反映在观点上。
  • OneWayToSource:当视图改变一个值时使用。 值更改应该被推到视图模型中。
  • two - way:数据流是双向的。
  • OneTime:绑定上下文被绑定后,数据同步只发生一次,并且数据已经从视图模型传播到视图。

有了这个信息,用户名和密码字段应该使用OneWayToSource绑定,而消息标签应该使用OneWay绑定模式,因为结果只由视图模型更新。

下一步是设置要执行的函数的命令(即登录和注册)。 从语义上讲,命令由一个方法(包含它的数据和/或参数)和一个状态(它是否可以被执行)组成。 该结构由ICommand接口描述:

public interface ICommand
{
   void Execute(object arg);
   bool CanExecute(object arg);
   event EventHandler CanExecuteChanged;
}

Xamarin 的。 窗体,该接口有两种实现:CommandCommand<T>。 使用这些类中的任何一个,都可以完成命令绑定。 例如,为了将Login方法作为命令公开,请遵循以下步骤:

  1. 首先,在LoginViewModel

    ``` private Command _loginCommand;

    public ICommand LoginCommand { get { return _loginCommand; } } ```

    中声明我们的Commandpr 属性 2. In order to initialize _loginCommand, use the following constructor:

    public LoginViewModel() { _loginCommand = new Command(Login, Validate); }

    注意,我们使用了两个操作来初始化该命令。 的第一个参数,这是Action类型,是实际的方法执行,而第二个参数,是Func<bool>的类型,是一个方法引用,返回一个指示Boolean该方法能否被执行。

  2. Validate方法的实现应该像这样:k

  3. 最后,为了完成实现,当UserNamePassword字段发生变化时,发送CanExecuteChanged事件:

    ``` public string UserName { get { return _userName; } set { if (_userName != value) { _userName = value; _loginCommand.ChangeCanExecute(); } } }

    public string Password { get { return _password; } set { if (_password != value) { _password = value; _loginCommand.ChangeCanExecute(); } } } ```

  4. Now, add a command binding to the Login button so that the created LoginCommand is utilized by the view:

    <Button x:Name="loginButton" Text="Login" Command="{Binding LoginCommand}"/>

    现在,如果您要运行应用,您将看到命令的禁用和启用状态如何反映在 UI 上:

    Figure 4.8 – MVVM command binding

    图 4.8 - MVVM 命令绑定

    现在命令已经设置好了,我们只有结果消息绑定,它是,仍然没有按预期工作。 此时,点击Login按钮将更新视图模型数据,但用户界面不会反映此数据更改。 这样做的原因是该字段应该用OneWay绑定绑定(源中的更改应该反映在目标上)。 这样做的主要要求是源(视图模型)应该实现在System.ComponentModel名称空间中定义的INotifyPropertyChanged接口。 INotifyPropertyChanged是将绑定上下文上的更改传播到视图元素的基本机制:

    /// <summary> Notifies clients that a property value has changed. </summary> public interface INotifyPropertyChanged { /// <summary> Occurs when a property value changes. </summary> event PropertyChangedEventHandler PropertyChanged; }

    一个简单的实现需要调用带有当前正在更改的属性的PropertyChanged事件。

    重要提示

    如果您对一个属性所做的更改影响了多个数据点(例如,分配一个列表数据源更改了项计数属性),那么视图模型将负责为 UI 需要使其失效的所有属性触发相同的事件。

  5. 通过引入接口实现并在Result属性的 setter 上使用事件触发器,我们应该能够看到Login命令的结果:

    ``` public class LoginViewModel : INotifyPropertyChanged { // … cont'd public event PropertyChangedEventHandler PropertyChanged;

    public string Result
    {
        get
        {
            return _result;
        }
        set
        {
            if (_result != value)
            {
                _result = value;
                PropertyChanged?.Invoke(this, new 
                PropertyChangedEventArgs(nameof (Result)));
            }
        }
    }
    

    } ```

这就完成了登录视图的视图模型实现。 现在我们可以为LoginViewModel类创建单元测试,并使用额外的异常处理和其他必要的扩展来扩展实现。

单向数据流的架构模式

在我们已经实现的 mediationController 和 MVVM 模式中,随着视图复杂性的增加,数据流路径变得难以想象的复杂。 这种复杂性主要是由于视图模型的双工特性,这降低了视图模型的可预见性。 在一个复杂的视图中,即使视图模型仍然保留固有的有限自动机属性,因为随着引入的每个双向绑定,状态的数量呈指数增长,代码的可维护性直线下降。

作为对这个复杂性问题的响应,在数据驱动的应用项目中,可以使用其他几个 MVC 衍生工具来强制执行单向数据流。 简单地说,这些模式旨在实现单向数据流,即将应用数据视为不可变项,并通过用户输入和/或数据触发器将应用状态更新为新状态。

通量结构是对双向数据问题的响应之一。 在 Flux 模型中,应用状态是在不可变数据存储中管理的。 然后,应用域可以查询这些数据存储,以及修改这些存储(也就是说,通过创建新的不可变版本),使用 reducer:

Figure 4.9 – Flux Architecture

图 4.9 -通量结构

Xamarin 的。 形式,如前面所示图中,通量的构件(如数据存储操作(也称为异径接头),和调度员可以引入现有的 MVVM 模式。 通过创建数据存储来管理应用数据,并将数据从数据存储定向到视图模型,可以实现单向数据流。 为了处理用户交互,我们可以创建小的操作块,这些操作块由命令触发,以分派 reducer,然后更改数据存储。**

*Xamarin 中使用的其他单向架构模型。 表单是模型-视图-更新(由 f#语言普及)和模型-视图-意图(特别是在 Android 应用中使用)。

在本节中,我们仔细研究了用于演示结构的不同架构模式。 我们还使用 MVC 和 MVVM 实现了应用的第一个视图。 我们创建了一个设置,视图负责创建视图模型; 然而,通过使用反转(IoC)的实现,例如依赖注入或服务定位器模式,视图可以取消此职责。 在下一节中,我们将仔细研究 Xamarin 中其他突出的架构模式。 形式的生态系统。

有用的架构模式

Xamarin 的。 表单作为一个框架,包含帮助开发人员实现知名架构模式的模块,这样他们就可以创建可维护且健壮的应用。 在这一节中,我们将仔细研究一些最重要的架构/设计模式; 也就是控制反转、事件聚合器和装饰器。

控制反转

IoC 是一种设计原则,其中为类的依赖关系选择具体实现的职责被委托给外部组件或源。 通过这种方式,类与它们的依赖关系是分离的,这样它们就可以被替换/更新而不需要太多麻烦。

这一原则最常见的实现是使用服务定位器模式,其中创建一个容器来存储具体的实现。 这通常是通过适当的抽象来注册的,如下图所示:

Figure 4.10 – Inversion of Control in Xamarin.Forms

图 4.10 - Xamarin 的控制反转 形式

Xamarin 的。 表单提供了DependencyService,当您为平台无关的需求创建平台特定的实现时,尤其有用。 我们还必须记住,它只能在 Xamarin 中使用。 形式平台项目; 否则,我们将创建一个不必要的 Xamarin 依赖。 表单库。

正如您所看到的,控制反转不仅有助于减少模块间的耦合,而且还允许我们抽象特定于平台的组件。

事件聚合器

事件聚合器(也称为发布者/订阅者或 Pub-Sub)是一种消息传递模式,其中消息/事件的发送方(称为发布者)不编程将消息直接发送到单个/特定的接收者(称为订阅者),而是: 将发布的消息分类到类中,而不知道可能存在哪些订阅者(如果有的话)。 然后通过所谓的聚合器将消息汇集起来,并传递给订阅者。 这种方法在保持有效的消息传递通道的同时,提供了发布者和订阅者之间的完全解耦。

事件聚合器在 Xamarin 中的实现。 表单框架通过MessagingCenter完成。 MessagingCenter公开了一个简单的 API,它由两个用于订阅者的方法(即订阅和取消订阅)和一个用于发布者的方法(发送)组成。

我们可以演示如何应用事件聚合器模式,方法是为应用中的服务通信或身份验证问题创建一个简单的事件接收器。 在这个事件接收器(它将是我们的订阅者)中,我们可以订阅从各种视图模型(假定它们都实现相同的基类型)接收到的错误消息,并使用友好的对话框提醒用户:

MessagingCenter.Subscribe<BaseViewModel> (this, "ServiceError", (sender, arg) =>
    {
        // TODO: Handler the error
    });

在视图模型中,未处理异常的事件中会出现以下情况:

 public void Login()
 {
     try
     {
         //TODO: Login
         Result = "Successfully Logged In!";
     }
     catch(Exception ex)
     {
         MessagingCenter.Send(this, "ServiceError", ex.Message);
     }
 } 

MessagingC``enter允许我们连接到不同的层,几乎在应用中创建一个消息总线。 如图所示,在 Xamarin 中已经为您提供了这个体系结构模式的基础结构。 形式框架。

装饰师

装饰器模式是一种设计模式,它允许行为动态地添加到单个对象中,而不影响来自同一类的其他对象的行为。 Xamarin 的。 表单利用此模式使用平台无关的可视元素(XAML 中使用的视图),并将呈现程序附加到这些元素上,这些元素定义了在目标平台上呈现它们的方式(创建本机特定于平台的控件)。 Xamarin 的成分。 表单元素不会改变渲染器的行为,反之亦然,它允许开发人员创建自定义的渲染器,并将它们附加到视图中,而不影响其他可视元素。 下图显示了渲染器类与装饰器模式交互的抽象:

Figure 4.11 – Decorator Pattern in Xamarin.Forms

图 4.11 -装饰模式在 Xamarin。 形式

如你所见,每个 Xamarin。 表单控件由共享/标准域中的视图定义表示。 该控件引用视图呈现器的特定抽象,然后在目标平台上使用给定视图呈现的特定于平台的实现。 在这种设置中,派生的自定义控件实现立即继承了基本控件的视图呈现器关联。 自定义渲染器,然而,需要导出一个特定的共享控件定义,以便布局引擎使用。

类似的方法也用于创建所谓的效果,即附加于现有视觉元素及其对应元素的简单行为修饰符。

总结

在本章中,我们深入探讨了实现 Xamarin 应用的架构方面,并为 MVVM 应用建立了基础。 为了演示不同表示架构的实现,我们使用 MVC 和 MVVM 模式实现了登录视图。 正如您所看到的,虽然这两种模式都可以用于 Xamarin。 表单应用,每种表单应用对视图和控制器之间的交互都有不同的理解,每种表单应用都有优点和缺点。 此外,其他模式,如 MVU 和 Flux,可以进一步改善项目的可维护性。 我们还简要地浏览了实现 Xamarin 应用可能需要的其他几个模式。 您现在应该能够创建一个 Xamarin。 从头创建应用,并使用适当的应用基础结构和体系结构为您的下一个项目设置样板解决方案。

在下一章中,我们将使用标准 Xamarin 实现应用的初始视图。 表单组件。 在本书的其余部分中,我们将尝试实现本章中讨论的应用组件。***