使用 Xamarin.Forms 保存奶奶的食谱
已发表: 2022-03-10我奶奶做的最好吃的,最蓬松的,去你膝盖上的小圆面包,任何人都吃过。 问题是,这些包子里有很多秘密成分(我不只是在谈论爱情),这些成分和说明都存储在我奶奶的脑海里。
我们都有这样的家庭食谱,而不是可能忘记它们,在本文中,我们将使用 Xamarin.Forms 创建一个适用于 iOS 和 Android 的移动应用程序,它将为我自己和我家人的后代保存它们!
因此,如果您对编写移动应用程序感兴趣,但没有时间为每个平台一遍又一遍地编写相同的应用程序,那么本文适合您! 如果您不了解草莓椒盐卷饼沙拉中的 C#,请不要担心; 我已经编写 Xamarin 应用程序超过 8 年,本文是 Xamarin.Forms 的导览,旨在为您提供足够的信息来开始自己学习。
这是什么 Xamarin 东西?
Xamarin 不仅仅是一个有趣的词,它允许开发人员使用与适用于 iOS 的 Swift 和 XCode 或适用于 Android 的 Java 和 Android Studio 中完全相同的 SDK 和 UI 控件来创建原生 iOS 和 Android 应用程序。
不同之处在于应用程序是使用 .NET Framework 和 Visual Studio 或 Visual Studio for Mac 使用 C# 开发的。 然而,产生的应用程序是完全相同的。 它们的外观、感觉和行为就像用 Objective-C、Swift 或 Java 编写的原生应用程序一样。
Xamarin 在代码共享方面大放异彩。 开发人员可以使用本机控件和 SDK 为每个平台创建和定制其 UI,然后编写一个跨平台共享的共享应用程序逻辑库。
正是这种代码共享可以节省大量时间。
就像我奶奶烤的美味面包一样,曾经有过共享代码的味道——很难不渴望更多——这就是 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 个屏幕组成。 第一个将显示应用程序中当前加载的所有食谱的列表。
然后,通过点击其中一个食谱,您将能够在第二个屏幕上查看其详细信息:
从那里您可以点击编辑按钮以在第三个屏幕上更改配方:
您还可以通过点击配方列表屏幕中的添加按钮进入此屏幕。
开发环境
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
也是应用内导航的中心。
我们通过Navigation Service告诉 Xamarin.Forms 要显示哪个Page
。 然后,该服务将负责以适合操作系统和本机的方式显示任何页面。
换句话说,在屏幕之间导航的代码也被抽象了!
最后,虽然不是唯一的方法,但我在 XAML 中对我的Page
的 UI 进行了编码。 (另一种方法是使用 C#。) XAML 是一种描述页面外观的标记语言。 现在,我只想说,它有点类似于 HTML。
布局
页面上的所有控件都由称为布局的东西排列。
一个或多个布局可以添加到页面。
表单中有几种不同类型的布局。 一些最常见的布局包括 Stack、Absolute、Relative、Grid、Scroll 和 Flex 布局。
控件
最后是控件。 这些是用户与之交互的应用程序的小部件。
表单带有许多控件,无论您正在构建什么类型的应用程序,这些控件都将被使用。 标签、按钮、输入框、图像,当然还有列表视图。
将控件添加到屏幕时,您将其添加到布局中。 它是负责确定控件应该出现在屏幕上的确切位置的布局。
因此分别在 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}"
。 其中Name
是Recipe
类的一个属性,它模拟......好吧,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
对象是一个独立于平台的对象,它以本机方式为每个平台处理页面转换。
现在让我们看一下食谱详细信息页面:
此页面也是使用 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
对象将其推送到我们的导航堆栈中。
食谱编辑页面
带有食谱列表的页面:检查! 包含制作食谱的所有详细信息的页面:检查! 现在剩下的就是创建我们用来输入或更改食谱的页面,同时我们看着奶奶施展她的魔法!
首先,查看屏幕:
现在代码:
<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
属性了吗? 它是页面级别的,保存特定配方的所有数据,并在页面的构造函数中设置。
其次, saveButton
和cancelButton
的Clicked
事件处理程序完全是 .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-wrap
或flex-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 的更多信息,包括在浏览器中创建应用程序的能力,请查看此在线教程!