使用 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 的更多信息,包括在瀏覽器中創建應用程序的能力,請查看此在線教程!