使用 Xamarin.Forms 保存奶奶的食谱

已发表: 2022-03-10
快速总结↬创建移动应用程序时,您必须分别为 iOS 和 Android 创建和维护用户界面和应用程序逻辑:Objective-C/Swift 与 XCode 和 Java 与 Android Studio。 这很快就会变成一种痛苦。 但是,使用 Xamarin.Forms,您的应用程序的 UI 和应用程序逻辑驻留在单个代码库中,您可以使用单个 IDE 来维护这一切 — 节省时间和麻烦。 在本文中,围绕 Xamarin.Forms 了解一下它可以为您做什么。

我奶奶做的最好吃的,最蓬松的,去你膝盖上的小圆面包,任何人都吃过。 问题是,这些包子里有很多秘密成分(我不只是在谈论爱情),这些成分和说明都存储在我奶奶的脑海里。

我们都有这样的家庭食谱,而不是可能忘记它们,在本文中,我们将使用 Xamarin.Forms 创建一个适用于 iOS 和 Android 的移动应用程序,它将为我自己和我家人的后代保存它们!

蒸汽上升的包子的绘图
好吃的暖包子(大预览)

因此,如果您对编写移动应用程序感兴趣,但没有时间为每个平台一遍又一遍地编写相同的应用程序,那么本文适合您! 如果您不了解草莓椒盐卷饼沙拉中的 C#,请不要担心; 我已经编写 Xamarin 应用程序超过 8 年,本文是 Xamarin.Forms 的导览,旨在为您提供足够的信息来开始自己学习。

这是什么 Xamarin 东西?

Xamarin 不仅仅是一个有趣的词,它允许开发人员使用与适用于 iOS 的 Swift 和 XCode 或适用于 Android 的 Java 和 Android Studio 中完全相同的 SDK 和 UI 控件来创建原生 iOS 和 Android 应用程序。

跳跃后更多! 继续往下看↓
绘制简笔画,想知道他们应该为 iOS 还是 Android 开发
我应该为哪个平台开发? (大预览)

不同之处在于应用程序是使用 .NET Framework 和 Visual Studio 或 Visual Studio for Mac 使用 C# 开发的。 然而,产生的应用程序是完全相同的。 它们的外观、感觉和行为就像用 Objective-C、Swift 或 Java 编写的原生应用程序一样。

Xamarin 在代码共享方面大放异彩。 开发人员可以使用本机控件和 SDK 为每个平台创建和定制其 UI,然后编写一个跨平台共享的共享应用程序逻辑库。

使用 Xamarin 为两个平台同时开发的想法绘制简笔画
啊哈! 我会选择 Xamarin! (大预览)

正是这种代码共享可以节省大量时间。

就像我奶奶烤的美味面包一样,曾经有过共享代码的味道——很难不渴望更多——这就是 Xamarin.Forms 的用武之地。

Xamarin.Forms

Xamarin.Forms 采用了传统 Xamarin 开发的概念,并为其添加了一层抽象。

Xamarin.Forms 没有单独为 iOS 和 Android 开发用户界面,而是引入了一个 UI 工具包,使您能够从单个代码库编写本机移动应用程序。

可以这样想:你有一个需要一个按钮的应用程序。 每个平台都有一个按钮的概念。 当您知道您的应用程序的所有用户需要做的就是点击一个按钮时,为什么还要多次编写用户界面?

这是 Xamarin.Forms 解决的问题之一。

它为它们提供了最常用的控件和用户交互事件的工具包,因此我们只需为我们的应用程序编写一次用户界面。 值得注意的是,您不仅限于 Xamarin.Forms 提供的控件 — 您仍然可以使用仅在 Xamarin.Forms 应用程序内的单个平台中找到的控件。 此外,我们可以像以前一样在平台之间共享应用程序逻辑。

使用 Xamarin.Forms 开发的应用程序的代码共享统计数据可能超出图表。 一个会议组织应用程序有 93% 的代码在 iOS 上共享,91% 在 Android 上共享。 该应用程序是开源的。 看一眼代码。

Xamarin.Forms 提供的不仅仅是 UI 控件。 它还包含一个 MVVM 框架、一个发布/订阅消息服务、一个动画 API 和一个依赖服务,以及其他。

但是今天,我们将专注于构建我们的食谱管理器应用程序的 UI 功能。

我们将构建的应用程序

配方管理器应用程序将有一个简单的用户界面。 我们将在厨房工作,所以它需要易于使用!

它将由 3 个屏幕组成。 第一个将显示应用程序中当前加载的所有食谱的列表。

iOS 上的食谱列表屏幕截图
(大预览)

然后,通过点击其中一个食谱,您将能够在第二个屏幕上查看其详细信息:

iOS 上的配方详情屏幕截图
iOS 上的食谱详情屏幕(大预览)

从那里您可以点击编辑按钮以在第三个屏幕上更改配方:

iOS 上的配方编辑屏幕截图
iOS 上的食谱编辑屏幕(大预览)

您还可以通过点击配方列表屏幕中的添加按钮进入此屏幕。

开发环境

Xamarin 应用使用 C# 和 .NET 构建,在 Windows 上使用 Visual Studio 或在 Mac 上使用 Visual Studio for Mac,但您还需要安装 iOS 或 Android SDK 和工具。 以正确的顺序安装所有东西可能有点问题,但是,Visual Studio 安装程序会注意只安装 IDE,还要安装平台工具。

尽管构建 iOS 应用程序始终需要 Mac,但您仍然可以使用 Xamarin 在 Windows 上从 Visual Studio 开发和调试这些应用程序! 因此,如果 Windows 是您的难题,则无需完全更改您的环境。

现在让我们看看 Xamarin.Forms 如何帮助我们从一个代码库中保存一些家庭食谱!

配方列表页面:布局 UI

让我们从讨论如何为我们的食谱保存应用程序布局 UI 开始吧!

总体而言,Xamarin.Forms 中的每个屏幕都由 3 个元素组成。 Page 。 至少有一个称为Layout的元素。 并且至少有一个Control

这页纸

页面是同时承载屏幕上显示的所有内容的东西。 Page也是应用内导航的中心。

在 Xamarin.Forms 中表示页面的绘图
页面(大预览)

我们通过Navigation Service告诉 Xamarin.Forms 要显示哪个Page 。 然后,该服务将负责以适合操作系统和本机的方式显示任何页面。

换句话说,在屏幕之间导航的代码也被抽象了!

最后,虽然不是唯一的方法,但我在 XAML 中对我的Page的 UI 进行了编码。 (另一种方法是使用 C#。) XAML 是一种描述页面外观的标记语言。 现在,我只想说,它有点类似于 HTML。

布局

页面上的所有控件都由称为布局的东西排列。

表示 Xamarin.Forms 中的一些布局的绘图
布局(大预览)

一个或多个布局可以添加到页面。

绘制布局如何与页面交互
页面布局(大预览)

表单中有几种不同类型的布局。 一些最常见的布局包括 Stack、Absolute、Relative、Grid、Scroll 和 Flex 布局。

绘制多个 Xamarin.Forms 布局以及它们如何排列其子元素。
常见 Xamarin.Forms 布局(大预览)

控件

最后是控件。 这些是用户与之交互的应用程序的小部件。

绘制几个 Xamarin.Forms 控件,每个控件都绘制为一个框
一些控件(大预览)

表单带有许多控件,无论您正在构建什么类型的应用程序,这些控件都将被使用。 标签、按钮、输入框、图像,当然还有列表视图。

将控件添加到屏幕时,您将其添加到布局中。 它是负责确定控件应该出现在屏幕上的确切位置的布局。

绘制包含 2 个布局的页面,以及根据布局类型排列控件的那些布局。
一切都融为一体! (大预览)

因此分别在 iOS 和 Android 上生成以下屏幕:

iOS 和 Android 上的食谱列表屏幕截图
iOS(左)和 Android(右)上的食谱列表(大预览)

我使用了这个 XAML:

 <?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="https://xamarin.com/schemas/2014/forms" xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml" x:Class="SmashingRecipe.RecipeListPage" Title="Recipes"> <ContentPage.Content> <StackLayout> <ListView x:Name="recipesList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding Name}"/> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage.Content> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" /> </ContentPage.ToolbarItems> </ContentPage>

这里有几件重要的事情正在发生。

第一个是<StackLayout> 。 这是告诉 Forms 安排堆栈中的所有控件。

布局中恰好只有一个控件,那就是<ListView> ,我们将给它一个名称,以便以后可以引用它。

在我们得到我们所追求的之前, ListView有一点样板仪式: <TextCell> 。 这是告诉表单在列表的每个单元格中显示简单的文本。

我们通过一种称为数据绑定的技术告诉<TextCell>我们希望它显示的文本。 语法类似于Text="{Binding Name}" 。 其中NameRecipe类的一个属性,它模拟......好吧,Recipes。

那么如何将食谱添加到列表中呢?

除了每个 XAML 文件,还有一个“代码隐藏”文件。 此代码隐藏允许我们执行诸如处理用户交互事件、执行设置或执行其他应用程序逻辑之类的操作。

在每个Page中都有一个可以被覆盖的函数OnAppearing我相信你猜到了——在Page出现时被调用。

 protected override void OnAppearing() { base.OnAppearing(); recipesList.ItemsSource = null; recipesList.ItemsSource = App.AllRecipes; }

注意recipesList.ItemsSource = AllRecipes;

这是在告诉ListView “嘿! 您的所有数据都在可枚举的App.AllRecipes (应用程序范围的变量)中找到,您可以使用其任何子对象的属性来绑定!”。

一份食谱清单很好——但你不能在没有先看到食谱细节的情况下烘烤任何东西——接下来我们将处理这个问题。

事件处理

在不响应用户触摸的情况下,我们的应用程序只不过是一个听起来很美味的食谱列表。 它们听起来不错,但不知道如何烹饪它们,并没有多大用处!

让我们让ListView中的每个单元格响应点击,这样我们就可以看到如何制作食谱了!

RecipeListPage代码隐藏文件中,我们可以将事件处理程序添加到控件以侦听和响应用户交互事件。

然后处理列表视图上的点击事件:

 recipesList.ItemSelected += async (sender, eventArgs) => { if (eventArgs.SelectedItem != null) { var detailPage = new RecipeDetailPage(eventArgs.SelectedItem as Recipe); await Navigation.PushAsync(detailPage); recipesList.SelectedItem = null; } };

那里有一些整洁的东西。

每当有人选择一行时,就会在ListView上触发ItemSelected

在传递给处理程序的参数中, eventArgs对象有一个SelectedItem属性,该属性恰好是之前绑定到ListView的任何内容。

在我们的例子中,这就是Recipe类。 (所以我们不必在主源中搜索对象 - 它会传递给我们。)

食谱详情页面

当然,有一个页面向我们展示了如何制作每个食谱的秘密成分和说明,但是该页面是如何显示的呢?

作为准备开始烘烤的厨师打扮的简笔画。
我们开始做饭吧! (大预览)

注意await Navigation.PushAsync(detailPage); 从上面的线。 Navigation对象是一个独立于平台的对象,它以本机方式为每个平台处理页面转换。

现在让我们看一下食谱详细信息页面:

iOS 和 Android 上的食谱详细信息屏幕截图
iOS(左)和 Android(右)上的食谱详情屏幕(大预览)

此页面也是使用 XAML 构建的。 但是,使用的Layout (FlexLayout)非常酷,因为它的灵感来自 CSS Flexbox。

 <ContentPage.Content> <ScrollView> <FlexLayout AlignItems="Start" AlignContent="Start" Wrap="Wrap"> <Image Source="buns.png" FlexLayout.Basis="100%" /> <Label Text="{Binding Name}" HorizontalTextAlignment="Center" TextColor="#01487E" FontAttributes="Bold" FontSize="Large" Margin="10, 10" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="Ingredients" FontAttributes="Bold" FontSize="Medium" TextColor="#EE3F60" Margin="10,10" HorizontalOptions="FillAndExpand" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="{Binding Ingredients}" Margin="10,10" FontSize="Small" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="Directions" FontAttributes="Bold" FontSize="Medium" TextColor="#EE3F60" Margin="10,10" HorizontalOptions="FillAndExpand" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="{Binding Directions}" Margin="10,10" FontSize="Small" /> </FlexLayout> </ScrollView> </ContentPage.Content>

FlexLayout将其控件排列在行或列中。 最大的好处是它可以自动检测屏幕上还有多少空间可以放置控件,如果没有足够的空间,它可以自动创建一个新的行或列来容纳它!

这在处理各种屏幕尺寸时有很大帮助,在移动开发中有很多。

好吧,有了FlexLayout帮助我们保持细节屏幕看起来不错,我们仍然需要编辑这些食谱,对吧?

你可能注意到了这一点:

 <ToolbarItem Text="Edit" Clicked="Edit_Clicked" />

该行负责在应用程序的工具栏中放置一个按钮。 Clicked="Edit_Clicked"告诉按钮,当它被单击时,在后面的代码中查找该名称的函数,然后执行其代码。

在这种情况下,将实例化食谱编辑页面,并使用前面提到的Navigation对象将其推送到我们的导航堆栈中。

食谱编辑页面

带有食谱列表的页面:检查! 包含制作食谱的所有详细信息的页面:检查! 现在剩下的就是创建我们用来输入或更改食谱的页面,同时我们看着奶奶施展她的魔法!

首先,查看屏幕:

iOS 和 Android 上的配方编辑屏幕截图
iOS(左)和 Android(右)上的配方编辑屏幕(大预览)

现在代码:

 <ContentPage.Content> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TableView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Intent="Form" HasUnevenRows="true"> <TableSection Title="General"> <EntryCell x:Name="recipeNameCell" Label="Name" /> </TableSection> <TableSection Title="Ingredients"> <ViewCell> <StackLayout Padding="15"> <Editor x:Name="ingredientsCell" /> </StackLayout> </ViewCell> </TableSection> <TableSection Title="Directions"> <ViewCell> <StackLayout Padding="15"> <Editor x:Name="directionsCell" /> </StackLayout> </ViewCell> </TableSection> </TableView> <Button Text="Save" Grid.Row="1" Grid.Column="0" BackgroundColor="#EE3F60" TextColor="White" x:Name="saveButton" /> <Button Text="Cancel" Grid.Row="1" Grid.Column="1" BackgroundColor="#4CC7F2" TextColor="White" x:Name="cancelButton" /> </Grid> </ContentPage.Content>

这里还有一些代码,那是因为我使用Grid布局来指定所有内容应如何以二维模式布局。

还要注意这里没有数据绑定。 因为我想举一个例子,说明如何完全从文件隐藏的代码中填充控件:

 void InitializePage() { Title = TheRecipe.Name ?? "New Recipe"; recipeNameCell.Text = TheRecipe.Name; ingredientsCell.Text = TheRecipe.Ingredients; directionsCell.Text = TheRecipe.Directions; saveButton.Clicked += async (sender, args) => { SaveRecipe(); await CloseWindow(); }; cancelButton.Clicked += async (sender, args) => { await CloseWindow(); }; }

看到TheRecipe属性了吗? 它是页面级别的,保存特定配方的所有数据,并在页面的构造函数中设置。

其次, saveButtoncancelButtonClicked事件处理程序完全是 .NET 化的(是的,我确实经常自己造词。)

我说它们是 .NET 化的,因为处理该事件的语法不是 Java 或 Objective-C 原生的。 当应用程序在 Android 或 iOS 上运行时,该行为将与 Android Click 或 iOS TouchUpInside 完全相同。

正如您所看到的,每个单击事件处理程序都在调用适当的函数,这些函数要么保存配方并关闭页面,要么只关闭页面。

就是这样——我们已经放下 UI 来保存从现在到时间结束的食谱!

CSS 什么?!? 或使应用程序漂亮

将最好的留到最后:Xamarin.Forms 3.0 为我们提供了(除其他外)使用 CSS 设置控件样式的能力!

Xamarin.Forms CSS 并非 100% 您在 Web 开发中所习惯的。 但它足够接近,任何熟悉 CSS 的人都会有宾至如归的感觉。 就像我在奶奶家一样!

因此,让我们使用“配方详细信息”页面并对其进行重构,以便它使用级联样式表来设置视觉元素,而不是直接在 XAML 中内联设置所有内容。

第一步是创建 CSS 文档! 在这种情况下,它将如下所示:

 .flexContent { align-items: flex-start; align-content: flex-start; flex-wrap: wrap; } image { flex-basis: 100%; } .spacer { flex-basis: 100%; height: 0; } .foodHeader { font-size: large; font-weight: bold; color: #01487E; margin: 10 10; } .dataLabel { font-size: medium; font-weight: bold; color: #EE3F60; margin: 10 10; } .data { font-size: small; margin: 10 10; }

在大多数情况下,它看起来像 CSS。 里面有课。 类类型Image有一个选择器。 然后是一堆属性设置器。

其中一些属性设置器(例如flex-wrapflex-basis )特定于 Xamarin.Forms。 展望未来,团队将在前面加上xf-以遵循标准做法。

接下来是将其应用于 XAML 控件。

 <ContentPage.Resources> <StyleSheet Source="../Styles/RecipeDetailStyle.css" /> </ContentPage.Resources> <ContentPage.Content> <ScrollView> <FlexLayout StyleClass="flexContent"> <Image Source="buns.png" /> <Label Text="{Binding Name}" StyleClass="foodHeader" /> <BoxView StyleClass="spacer" /> <Label Text="Ingredients" StyleClass="dataLabel" HorizontalOptions="FillAndExpand" /> <BoxView StyleClass="spacer" /> <Label Text="{Binding Ingredients}" StyleClass="data" /> <BoxView StyleClass="spacer" /> <Label Text="Directions" StyleClass="dataLabel" HorizontalOptions="FillAndExpand" /> <BoxView StyleClass="spacer" /> <Label Text="{Binding Directions}" StyleClass="data" /> </FlexLayout> </ScrollView> </ContentPage.Content>

这是它以前的样子。

在 Xamarin.Forms 中,要引用 CSS 文档,请添加<StyleSheet Source="YOUR DOC PATH" /> 。 然后,您可以通过StyleClass属性引用每个控件中的类。

它确实清理了 XAML,并且也使控件的意图更加清晰。 例如,现在很明显这些<BoxView StyleClass="spacer" />在做什么!

并且Image本身被设置了样式,因为它是一个Image以及我们在 CSS 中定义选择器的方式。

可以肯定的是,Xamarin.Forms 中的 CSS 没有像它的 web 表亲那样完全实现,但它仍然很酷。 你有选择器、类、可以设置属性,当然还有整个级联的事情!

概括

三屏、两个平台、一篇文章、无尽的食谱保存! 你还知道什么吗? 你可以使用 Xamarin.Forms 为 Android 和 iOS 以外的其他应用程序构建应用程序。 您可以构建 UWP、macOS 甚至三星 Tizen 平台!

蒸汽上升的包子的绘图
美味的! (大预览)

Xamarin.Forms 是一个 UI 工具包,它允许您通过编写一次用户界面并在主要平台上以本机方式呈现 UI 来创建应用程序。

它通过提供一个 SDK 来做到这一点,该 SDK 是跨平台最常用控件的抽象。 除了良好的 UI 之外,Xamarin.Forms 还提供了一个功能齐全的 MVVM 框架、一个发布/订阅消息服务、一个动画 API 和一个依赖项服务。

Xamarin.Forms 还为您提供与传统 Xamarin 开发相同的所有代码优势。 任何应用程序逻辑都在所有平台之间共享。 而且您可以使用单一语言使用单一 IDE 开发所有应用程序——这非常酷!

下一步去哪里? 下载此 Xamarin.Forms 应用程序的源代码,亲自试一试。 然后要了解有关 Xamarin.Forms 的更多信息,包括在浏览器中创建应用程序的能力,请查看此在线教程!