Salvare le ricette della nonna con Xamarin.Forms
Pubblicato: 2022-03-10Mia nonna prepara i panini migliori, più soffici e deboli che qualcuno abbia mai assaggiato. Il problema è che ci sono un sacco di ingredienti segreti (e non sto solo parlando di amore) che entrano in quei panini, e quegli ingredienti e quelle indicazioni sono tutti immagazzinati nella testa di mia nonna.
Tutti abbiamo ricette di famiglia del genere e, invece di dimenticarle, in questo articolo creeremo un'app mobile per iOS e Android usando Xamarin.Forms che le salverà per me e per le future generazioni della mia famiglia!

Quindi, se sei interessato a scrivere applicazioni mobili, ma non hai il tempo di scrivere la stessa app più e più volte per ogni piattaforma, questo articolo è per te! Non preoccuparti se non conosci C# da un'insalata di pretzel alla fragola; Scrivo app Xamarin da oltre 8 anni e questo articolo è un tour di Xamarin.Forms che intende fornirti informazioni sufficienti per iniziare a imparare da solo.
Cos'è questa roba Xamarin?
Più che una semplice parola divertente da dire, Xamarin consente agli sviluppatori di creare applicazioni iOS e Android native utilizzando esattamente gli stessi SDK e controlli dell'interfaccia utente disponibili in Swift e XCode per iOS o Java e Android Studio per Android.

La differenza è che le app vengono sviluppate con C# usando .NET Framework e Visual Studio o Visual Studio per Mac. Le app che ne risultano, tuttavia, sono esattamente le stesse. Sembrano, si sentono e si comportano proprio come le app native scritte in Objective-C, Swift o Java.
Xamarin brilla quando si tratta di condivisione del codice. Uno sviluppatore può creare e personalizzare la propria interfaccia utente per ogni piattaforma usando controlli e SDK nativi, ma poi scrivere una libreria di logica dell'app condivisa condivisa tra le piattaforme.

È questa condivisione del codice in cui è possibile realizzare enormi risparmi di tempo.
E come i deliziosi panini che prepara mia nonna, una volta dato il gusto di condividere il codice - è difficile non desiderarne di più - ed è qui che entra in gioco Xamarin.Forms.
Xamarin.Forms
Xamarin.Forms prende il concetto di sviluppo Xamarin tradizionale e aggiunge un livello di astrazione.
Invece di sviluppare separatamente l'interfaccia utente per iOS e Android, Xamarin.Forms introduce un toolkit dell'interfaccia utente che consente di scrivere app mobili native da un'unica base di codice.
Pensala in questo modo: hai un'app che ha bisogno di un pulsante. Ogni piattaforma ha il concetto di pulsante. Perché dovresti scrivere l'interfaccia utente un sacco di volte quando sai che tutto ciò che l'utente della tua app deve fare è toccare un pulsante?
Questo è uno dei problemi risolti da Xamarin.Forms.
Fornisce un toolkit dei controlli più comunemente utilizzati e degli eventi di interazione dell'utente per loro, quindi dobbiamo scrivere le interfacce utente per le nostre app solo una volta. Vale la pena notare, tuttavia, che non sei limitato ai controlli forniti da Xamarin.Forms: puoi comunque usare i controlli trovati solo in una singola piattaforma all'interno di un'app Xamarin.Forms. Inoltre, possiamo condividere la logica dell'applicazione tra le piattaforme come prima.
Le statistiche di condivisione del codice per le app sviluppate con Xamarin.Forms possono essere fuori scala. Un'app per l'organizzazione di conferenze ha il 93% del suo codice condiviso su iOS e il 91% su Android. L'app è open source. Dai un'occhiata al codice.
Xamarin.Forms offre più dei controlli dell'interfaccia utente. Contiene anche un framework MVVM, un servizio di messaggistica pub/sub, un'API di animazione e un servizio di dipendenza, oltre ad altri.
Ma oggi ci concentreremo sulle funzionalità dell'interfaccia utente per la creazione della nostra app di gestione delle ricette.
L'app che creeremo
L'app di gestione delle ricette avrà un'interfaccia utente semplice. Lavoreremo in cucina, quindi deve essere facile da usare!
Sarà composto da 3 schermate . Il primo mostrerà un elenco di tutte le ricette attualmente caricate nell'app.

Quindi, toccando una di quelle ricette, potrai vederne i dettagli su una seconda schermata:

Da lì puoi toccare un pulsante di modifica per apportare modifiche alla ricetta nella terza schermata:

Puoi anche accedere a questa schermata toccando il pulsante Aggiungi dalla schermata dell'elenco delle ricette.
L'ambiente di sviluppo
Le app Xamarin vengono compilate con C# e .NET, usando Visual Studio in Windows o Visual Studio per Mac nel Mac, ma è necessario che siano installati anche gli SDK e gli strumenti iOS o Android. Installare tutto nell'ordine corretto potrebbe essere un po' un problema, tuttavia, i programmi di installazione di Visual Studio si occuperanno solo dell'installazione dell'IDE, ma anche degli strumenti della piattaforma.
Sebbene sia sempre necessario un Mac per creare app iOS, con Xamarin puoi comunque sviluppare ed eseguire il debug di tali app da Visual Studio in Windows! Quindi, se Windows è la tua marmellata, non è necessario modificare del tutto i tuoi ambienti.
Ora vediamo come Xamarin.Forms può aiutarci a salvare alcune ricette di famiglia da una base di codice.
Pagina elenco ricette: struttura dell'interfaccia utente
Iniziamo parlando di come layout dell'interfaccia utente per la nostra app di salvataggio delle ricette!
Nel complesso, ogni schermata in Xamarin.Forms è composta da 3 elementi. Una Page
. Almeno un elemento chiamato Layout
. E almeno un Control
.
La pagina
La Pagina è la cosa che ospita tutto ciò che viene visualizzato sullo schermo in una volta. La Page
è anche centrale nella navigazione all'interno di un'app.

Diciamo a Xamarin.Forms quale Page
visualizzare tramite un servizio di navigazione . Quel servizio si occuperà quindi di visualizzare qualsiasi pagina in un modo appropriato e nativo per il sistema operativo.
In altre parole, anche il codice per navigare tra le schermate è stato astratto!
Infine, sebbene non sia l'unico modo per farlo, codifico l'interfaccia utente delle mie Page
in XAML. (L'altro modo sarebbe usare C#.) XAML è un linguaggio di markup che descrive l'aspetto di una pagina. E per ora, basti dire, è un po' simile all'HTML.
Lo schema
Tutti i controlli su una pagina sono organizzati da qualcosa chiamato Layout.

È possibile aggiungere uno o più layout a una pagina.

Esistono diversi tipi di layout nei moduli. Alcuni dei più comuni includono i layout Stack, Absolute, Relative, Grid, Scroll e Flex.

I controlli
Poi finalmente ci sono i controlli. Questi sono i widget della tua app con cui l'utente interagisce.

I moduli sono dotati di molti controlli che verranno utilizzati indipendentemente dal tipo di app che stai creando. Cose come etichette, pulsanti, caselle di immissione, immagini e, naturalmente, visualizzazioni elenco.
Quando aggiungi un controllo a una schermata, lo aggiungi a un layout. È il layout che si occupa di capire dove esattamente sullo schermo dovrebbe apparire il controllo.

Quindi per generare le seguenti schermate rispettivamente su iOS e Android:

Ho usato questo 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>
Ci sono un paio di cose importanti che accadono qui.
Il primo è <StackLayout>
. Questo sta dicendo a Forms di organizzare tutti i controlli che seguono in uno stack.
Capita che ci sia un solo controllo nel layout, e questo è un <ListView>
, e gli daremo un nome in modo da poterlo fare riferimento in seguito.
Poi c'è un po' di cerimonia standard per ListView
prima di arrivare a ciò che stiamo cercando: il <TextCell>
. Questo sta dicendo a Forms di visualizzare il testo semplice in ogni cella dell'elenco.
Diciamo a <TextCell>
il testo che vogliamo che visualizzi attraverso una tecnica chiamata Data Binding . La sintassi è simile a Text="{Binding Name}"
. Dove Name
è una proprietà di una classe Recipe
che modella... beh, Ricette.
Quindi, come vengono aggiunte le ricette all'elenco?
Insieme a ogni file XAML, c'è un file "code-behind". Questo code-behind ci consente di eseguire operazioni come gestire gli eventi di interazione dell'utente, eseguire la configurazione o eseguire altre logiche dell'app.

C'è una funzione che può essere sovrascritta in ogni Page
chiamata OnAppearing
- che come sono sicuro che hai intuito - viene chiamata quando viene visualizzata la Page
.
protected override void OnAppearing() { base.OnAppearing(); recipesList.ItemsSource = null; recipesList.ItemsSource = App.AllRecipes; }
Notare le recipesList.ItemsSource = AllRecipes;
Questo sta dicendo a ListView
: "Ehi! Tutti i tuoi dati si trovano App.AllRecipes
(una variabile a livello di applicazione) e puoi utilizzare una qualsiasi delle proprietà del suo oggetto figlio per associare!
Un elenco di ricette va bene, ma non puoi cuocere nulla senza prima vedere i dettagli della ricetta e di questo ci occuperemo in seguito.
Gestione degli eventi
Senza rispondere ai tocchi dell'utente, la nostra app non è altro che un elenco di deliziose ricette dal suono. Suonano bene, ma senza saperli cucinare non serve a molto!
Facciamo in modo che ogni cella in ListView
risponda ai tocchi in modo da poter vedere come realizzare la ricetta!
Nel file code-behind RecipeListPage
, possiamo aggiungere gestori di eventi ai controlli per ascoltare e reagire agli eventi di interazione dell'utente.
Gestione degli eventi di tocco nella visualizzazione elenco, quindi:
recipesList.ItemSelected += async (sender, eventArgs) => { if (eventArgs.SelectedItem != null) { var detailPage = new RecipeDetailPage(eventArgs.SelectedItem as Recipe); await Navigation.PushAsync(detailPage); recipesList.SelectedItem = null; } };
Ci sono cose belle che stanno succedendo lì.
Ogni volta che qualcuno seleziona una riga, ItemSelected
viene attivato su ListView
.
Degli argomenti che vengono passati al gestore, l'oggetto eventArgs
ha una proprietà SelectedItem
che è qualsiasi cosa sia associata a ListView
da prima.
Nel nostro caso, questa è la classe Recipe
. (Quindi non dobbiamo cercare l'oggetto nella sorgente principale - ci viene passato.)
Pagina dei dettagli della ricetta
Naturalmente, c'è una pagina che ci mostra gli ingredienti segreti e le indicazioni su come realizzare ogni ricetta, ma come viene visualizzata quella pagina?

Si noti l' await Navigation.PushAsync(detailPage);
linea dall'alto. L'oggetto Navigation
è un oggetto indipendente dalla piattaforma che gestisce le transizioni di pagina in modo nativo per ciascuna piattaforma.
Ora diamo un'occhiata alla pagina dei dettagli della ricetta:

Anche questa pagina è stata creata con XAML. Tuttavia, il Layout
utilizzato (FlexLayout) è piuttosto interessante poiché è ispirato al 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
organizzerà i suoi controlli in righe o colonne. Il grande vantaggio viene però dal fatto che può rilevare automaticamente quanto spazio è rimasto sullo schermo per posizionare un controllo e, se non ce n'è abbastanza, può creare automaticamente una nuova riga o colonna per adattarlo!
Questo aiuta molto quando si ha a che fare con schermi di varie dimensioni, che sono molte nello sviluppo mobile.
Bene, con FlexLayout
che ci aiuta a mantenere la schermata dei dettagli in buone condizioni, dobbiamo ancora modificare quelle ricette, giusto?
Probabilmente avrai notato questo:
<ToolbarItem Text="Edit" Clicked="Edit_Clicked" />
Quella riga è responsabile dell'inserimento di un pulsante nella barra degli strumenti dell'app. Clicked="Edit_Clicked"
dice al pulsante che quando viene cliccato, cerca nel codice dietro una funzione con quel nome, quindi esegui il suo codice.
Che in questo caso sarebbe istanziare la Pagina di modifica della ricetta e spingerla nel nostro stack di navigazione utilizzando l'oggetto Navigation
menzionato in precedenza.
Pagina Modifica Ricetta
Una pagina con un elenco di ricette: controlla! Una pagina con tutti i dettagli per realizzare le ricette: controlla! Non resta che creare la pagina che utilizziamo per inserire o modificare una ricetta mentre guardiamo la nonna fare la sua magia!
Per prima cosa, controlla le schermate:

E ora il codice:
<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>
C'è un po' più di codice qui, ed è perché sto usando il layout Grid
per specificare come tutto dovrebbe essere disposto in uno schema bidimensionale.
E anche qui non si notano dati vincolanti. Perché volevo fornire un esempio di come si possano popolare i controlli esclusivamente dal codice dietro il file:
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(); }; }
Vedi quella proprietà TheRecipe
? È a livello di pagina, contiene tutti i dati per una particolare ricetta e viene impostato nel costruttore della pagina.
In secondo luogo, i gestori di eventi Clicked
per saveButton
e cancelButton
sono totalmente .NET-ified (e sì, mi invento abbastanza spesso le mie parole).
Dico che sono .NET-ified perché la sintassi per gestire quell'evento non è nativa per Java né Objective-C. Quando l'app viene eseguita su Android o iOS, il comportamento sarà esattamente come un clic Android o un TouchUpInside iOS.
E come puoi vedere, ciascuno di quei gestori di eventi di clic sta invocando funzioni appropriate che salvano la ricetta e ignorano la pagina, o semplicemente eliminano la pagina.
Eccolo: abbiamo l'interfaccia utente in basso per salvare le ricette da ora fino alla fine dei tempi!
CSS Cosa?!? O rendere l'app carina
Salvare il meglio per ultimo: Xamarin.Forms 3.0 ci offre, tra le altre cose, la possibilità di applicare uno stile ai controlli usando CSS!
Il CSS di Xamarin.Forms non è al 100% ciò a cui potresti essere abituato dallo sviluppo Web. Ma è abbastanza vicino che chiunque abbia familiarità con i CSS si sentirà come a casa. Proprio come me dalla nonna!
Prendiamo quindi la pagina dei dettagli della ricetta e la rifattorizziamo, in modo che utilizzi i fogli di stile a cascata per impostare gli elementi visivi invece di impostare tutto direttamente in linea nell'XAML.
Il primo passo è creare il documento CSS! In questo caso sarà simile al seguente:
.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; }
Per la maggior parte, sembra CSS. Ci sono classi lì dentro. Esiste un unico selettore per un tipo di classe, Image
. E poi un gruppo di regolatori di proprietà.
Alcuni di questi setter di proprietà, ad esempio flex-wrap
o flex-basis
sono specifici di Xamarin.Forms. Andando avanti, il team prefiggerà quelli con xf-
per seguire le pratiche standard.
Il prossimo passo sarà applicarlo ai controlli 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>
Ecco com'era prima.
In Xamarin.Forms, per fare riferimento al documento CSS, aggiungi un <StyleSheet Source="YOUR DOC PATH" />
. Quindi puoi fare riferimento alle classi in ogni controllo tramite la proprietà StyleClass
.
Pulisce definitivamente l'XAML e rende anche più chiara l'intenzione del controllo. Ad esempio, ora è abbastanza ovvio cosa stanno facendo quei <BoxView StyleClass="spacer" />
!
E l' Image
ha lo stile stesso perché è Image
e il modo in cui abbiamo definito il selettore nel CSS.
A dire il vero, CSS in Xamarin.Forms non è completamente implementato come il suo cugino Web, ma è comunque piuttosto interessante. Hai selettori, classi, puoi impostare proprietà e, naturalmente, tutta quella cosa a cascata in corso!
Sommario
Tre schermate, due piattaforme, un articolo e infinite ricette salvate! E sai cos'altro? Puoi creare app con Xamarin.Forms per più di Android e iOS. Puoi creare piattaforme UWP, macOS e persino Samsung Tizen!

Xamarin.Forms è un toolkit dell'interfaccia utente che consente di creare app scrivendo l'interfaccia utente una volta e facendo eseguire il rendering dell'interfaccia utente in modo nativo sulle principali piattaforme.
Lo fa fornendo un SDK che è un'astrazione per i controlli più comunemente usati sulle piattaforme. Oltre alla bontà dell'interfaccia utente, Xamarin.Forms fornisce anche un framework MVVM completo, un servizio di messaggistica pub/sub, un'API di animazione e un servizio di dipendenza.
Xamarin.Forms offre anche tutti gli stessi vantaggi del codice offerti dallo sviluppo Xamarin tradizionale. Qualsiasi logica applicativa è condivisa su tutte le piattaforme. E puoi sviluppare tutte le tue app con un unico IDE usando un unico linguaggio: è davvero fantastico!
Dove andare? Scarica il codice sorgente per questa app Xamarin.Forms per provarla tu stesso. Quindi, per saperne di più su Xamarin.Forms, inclusa la possibilità di creare un'app all'interno del tuo browser, dai un'occhiata a questo tutorial online!