Guardar las recetas de la abuela con Xamarin.Forms
Publicado: 2022-03-10Mi abuela hace los mejores, más esponjosos, panecillos débiles en las rodillas que nadie haya probado. El problema es que hay un montón de ingredientes secretos (y no solo estoy hablando de amor) que van en esos bollos, y esos ingredientes e instrucciones están almacenados en la cabeza de mi abuela.
Todos tenemos recetas familiares como esa, y en lugar de posiblemente olvidarlas, en este artículo vamos a crear una aplicación móvil para iOS y Android usando Xamarin.Forms que las guardará para mí y para las futuras generaciones de mi familia.
Entonces, si está interesado en escribir aplicaciones móviles, pero no tiene tiempo para escribir la misma aplicación una y otra vez para cada plataforma, ¡este artículo es para usted! No se preocupe si no sabe C# de una ensalada de pretzel de fresa; He estado escribiendo aplicaciones de Xamarin durante más de 8 años, y este artículo es un recorrido por Xamarin.Forms que pretende brindarle suficiente información para comenzar a aprender por su cuenta.
¿Qué es esto de Xamarin?
Más que una simple palabra divertida, Xamarin permite a los desarrolladores crear aplicaciones nativas de iOS y Android utilizando exactamente los mismos SDK y controles de interfaz de usuario disponibles en Swift y XCode para iOS o Java y Android Studio para Android.
La diferencia es que las aplicaciones se desarrollan con C# utilizando .NET Framework y Visual Studio o Visual Studio para Mac. Las aplicaciones que resultan, sin embargo, son exactamente las mismas. Se ven, se sienten y se comportan como aplicaciones nativas escritas en Objective-C, Swift o Java.
Xamarin brilla cuando se trata de compartir código. Un desarrollador puede crear y adaptar su interfaz de usuario para cada plataforma utilizando controles nativos y SDK, y luego escribir una biblioteca de lógica de aplicación compartida que se comparte entre plataformas.
Es este código compartido donde se puede lograr un tremendo ahorro de tiempo.
Y como los deliciosos panes que hornea mi abuela, una vez que se le da el gusto de compartir el código, es difícil no desear más, y ahí es donde entra en juego Xamarin.Forms.
Xamarin.Forms
Xamarin.Forms toma el concepto del desarrollo tradicional de Xamarin y le agrega una capa de abstracción.
En lugar de desarrollar la interfaz de usuario para iOS y Android por separado, Xamarin.Forms presenta un kit de herramientas de interfaz de usuario que le permite escribir aplicaciones móviles nativas desde una única base de código.
Piénselo de esta manera: tiene una aplicación que necesita un botón. Cada plataforma tiene el concepto de un botón. ¿Por qué debería tener que escribir la interfaz de usuario varias veces cuando sabe que todo lo que el usuario de su aplicación debe hacer es tocar un botón?
Ese es uno de los problemas que resuelve Xamarin.Forms.
Proporciona un conjunto de herramientas de los controles más utilizados y los eventos de interacción del usuario para ellos, por lo que solo tenemos que escribir las interfaces de usuario para nuestras aplicaciones una vez. Sin embargo, vale la pena señalar que no está limitado a los controles que proporciona Xamarin.Forms; aún puede usar los controles que se encuentran en una sola plataforma dentro de una aplicación de Xamarin.Forms. Además, podemos compartir la lógica de la aplicación entre plataformas como antes.
Las estadísticas de uso compartido de código para aplicaciones desarrolladas con Xamarin.Forms pueden estar fuera de serie. Una aplicación de organización de conferencias tiene el 93 % de su código compartido en iOS y el 91 % en Android. La aplicación es de código abierto. Echa un vistazo al código.
Xamarin.Forms proporciona más que controles de interfaz de usuario. También contiene un marco MVVM, un servicio de mensajería pub/sub, una API de animación y un servicio de dependencia, entre otros.
Pero hoy nos vamos a centrar en las capacidades de la interfaz de usuario para crear nuestra aplicación de administrador de recetas.
La aplicación que construiremos
La aplicación del administrador de recetas tendrá una interfaz de usuario sencilla. ¡Estaremos trabajando en la cocina, por lo que debe ser fácil de usar!
Constará de 3 pantallas . El primero mostrará una lista de todas las recetas cargadas actualmente en la aplicación.
Luego, al tocar en una de esas recetas, podrá ver sus detalles en una segunda pantalla:
Desde allí, puede tocar un botón de edición para realizar cambios en la receta en la tercera pantalla:
También puede acceder a esta pantalla tocando el botón Agregar en la pantalla de la lista de recetas.
El entorno de desarrollo
Las aplicaciones de Xamarin se compilan con C# y .NET, con Visual Studio en Windows o Visual Studio para Mac en la Mac, pero también debe tener instalados los SDK y las herramientas de iOS o Android. Obtener todo instalado, en el orden correcto podría ser un problema, sin embargo, los instaladores de Visual Studio se encargarán de la nota solo de instalar el IDE, pero también las herramientas de la plataforma.
Aunque siempre se requiere una Mac para crear aplicaciones de iOS, ¡con Xamarin aún puede desarrollar y depurar esas aplicaciones desde Visual Studio en Windows! Entonces, si Windows es su atasco, no hay necesidad de cambiar sus entornos por completo.
¡Ahora veamos cómo Xamarin.Forms puede ayudarnos a guardar algunas recetas familiares desde una base de código!
Página de lista de recetas: diseño de la interfaz de usuario
¡Comencemos hablando sobre cómo vamos a diseñar la interfaz de usuario para nuestra aplicación para guardar recetas!
En general, cada pantalla en Xamarin.Forms se compone de 3 elementos. Una Page
. Al menos un elemento llamado Layout
. Y al menos un Control
.
La página
La página es lo que aloja todo lo que se muestra en la pantalla al mismo tiempo. La Page
también es central en la navegación dentro de una aplicación.
Le decimos a Xamarin.Forms qué Page
mostrar a través de un servicio de navegación . Ese servicio se encargará de mostrar cualquier página de una manera que sea apropiada y nativa para el sistema operativo.
En otras palabras, ¡el código para navegar entre pantallas también se ha abstraído!
Finalmente, aunque no es la única forma de hacerlo, codifico la interfaz de usuario de mi Page
en XAML. (La otra forma sería usar C#). XAML es un lenguaje de marcado que describe cómo se ve una página. Y por ahora, basta con decir que es un poco similar a HTML.
el diseño
Todos los controles en una página están organizados por algo llamado Diseño.
Se pueden agregar uno o más diseños a una página.
Hay varios tipos diferentes de diseños en formularios. Algunos de los más comunes incluyen diseños Stack, Absolute, Relative, Grid, Scroll y Flex.
los controles
Luego, finalmente están los controles. Estos son los widgets de su aplicación con los que interactúa el usuario.
Los formularios vienen con muchos controles que se usarán sin importar qué tipo de aplicación estés creando. Cosas como etiquetas, botones, cuadros de entrada, imágenes y, por supuesto, vistas de lista.
Al agregar un control a una pantalla, lo agrega a un diseño. Es el diseño el que se encarga de calcular exactamente en qué parte de la pantalla debe aparecer el control.
Entonces para generar las siguientes pantallas en iOS y Android respectivamente:
Usé este 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>
Hay un par de cosas importantes sucediendo aquí.
El primero es <StackLayout>
. Esto le dice a Forms que organice todos los controles que siguen en una pila.
Sucede que solo hay un único control en el diseño, y ese es un <ListView>
, y le daremos un nombre para que podamos hacer referencia a él más adelante.
Luego hay un poco de ceremonia repetitiva en ListView
antes de llegar a lo que buscamos: <TextCell>
. Esto le dice a Forms que muestre texto simple en cada celda de la lista.
Le decimos a <TextCell>
el texto que queremos que muestre a través de una técnica llamada Data Binding . La sintaxis se parece a Text="{Binding Name}"
. Donde Name
es una propiedad de una clase Recipe
que modela... bueno, Recetas.
Entonces, ¿cómo se agregan las recetas a la lista?
Junto con cada archivo XAML, hay un archivo de "código subyacente". Este código subyacente nos permite hacer cosas como manejar eventos de interacción del usuario, realizar la configuración o hacer otra lógica de la aplicación.
Hay una función que se puede anular en cada Page
llamada OnAppearing
, que, como seguramente habrás adivinado, se llama cuando aparece la Page
.
protected override void OnAppearing() { base.OnAppearing(); recipesList.ItemsSource = null; recipesList.ItemsSource = App.AllRecipes; }
Observe la lista de recipesList.ItemsSource = AllRecipes;
Esto le dice a ListView
: “¡Oye! ¡Todos sus datos se encuentran en App.AllRecipes
enumerable (una variable de toda la aplicación) y puede usar cualquiera de las propiedades de su objeto secundario para enlazar!”.
Una lista de recetas está muy bien, pero no puede hornear nada sin ver primero los detalles de la receta, y nos ocuparemos de eso a continuación.
Manejo de eventos
Sin responder a los toques de los usuarios, nuestra aplicación no es más que una lista de deliciosas recetas que suenan. Suenan bien, pero sin saber cocinarlos no sirve de mucho!
¡Hagamos que cada celda en ListView
responda a los toques para que podamos ver cómo hacer la receta!
En el archivo de código subyacente de RecipeListPage
, podemos agregar controladores de eventos a los controles para escuchar y reaccionar a los eventos de interacción del usuario.
Manejo de eventos de toque en la vista de lista y luego:
recipesList.ItemSelected += async (sender, eventArgs) => { if (eventArgs.SelectedItem != null) { var detailPage = new RecipeDetailPage(eventArgs.SelectedItem as Recipe); await Navigation.PushAsync(detailPage); recipesList.SelectedItem = null; } };
Están pasando algunas cosas geniales allí.
Cada vez que alguien selecciona una fila, ItemSelected
se activa en ListView
.
De los argumentos que se pasan al controlador, el objeto eventArgs
tiene una propiedad SelectedItem
que resulta ser lo que está vinculado al ListView
desde antes.
En nuestro caso, esa es la clase Recipe
. (Por lo tanto, no tenemos que buscar el objeto en la fuente maestra; se nos pasa).
Página de detalles de la receta
Por supuesto, hay una página que nos muestra los ingredientes secretos y las instrucciones de cómo hacer cada receta, pero ¿cómo se muestra esa página?
Observe la await Navigation.PushAsync(detailPage);
línea desde arriba. El objeto de Navigation
es un objeto independiente de la plataforma que maneja las transiciones de página de manera nativa para cada plataforma.
Ahora echemos un vistazo a la página de detalles de la receta:
Esta página también está construida con XAML. Sin embargo, el Layout
utilizado (FlexLayout) es bastante bueno ya que está inspirado en 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
organizará sus controles en filas o columnas. Sin embargo, el gran beneficio viene con el hecho de que puede detectar automáticamente cuánto espacio queda en la pantalla para colocar un control, y si no hay suficiente, ¡entonces puede crear automáticamente una nueva fila o columna para acomodarlo!
Esto es de gran ayuda cuando se trata de varios tamaños de pantalla, que hay muchos en el desarrollo móvil.
Bueno, con FlexLayout
ayudándonos a mantener la pantalla de detalles en buen estado, todavía tenemos que editar esas recetas, ¿verdad?
Probablemente hayas notado esto:
<ToolbarItem Text="Edit" Clicked="Edit_Clicked" />
Esa línea es la encargada de poner un botón en la barra de herramientas de la app. Clicked="Edit_Clicked"
le dice al botón que cuando se haga clic en él, busque en el código subyacente una función con ese nombre y luego ejecute su código.
Lo cual, en este caso, sería crear una instancia de la página de edición de recetas y empujarla a nuestra pila de navegación usando el objeto de Navigation
mencionado anteriormente.
Página de edición de recetas
Una página con una lista de recetas: comprobar! Una página con todos los detalles para hacer las recetas: consultar! ¡Todo lo que queda ahora es crear la página que usamos para ingresar o cambiar una receta mientras vemos a la abuela hacer su magia!
Primero, echa un vistazo a las pantallas:
Y ahora el código:
<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>
Aquí hay un poco más de código, y eso se debe a que estoy usando el diseño de Grid
para especificar cómo debe distribuirse todo en un patrón bidimensional.
Y también observe que no hay enlace de datos aquí. Porque quería dar un ejemplo de cómo uno llenaría los controles puramente desde el código detrás del archivo:
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(); }; }
¿Ves esa propiedad TheRecipe
? Es a nivel de página, contiene todos los datos de una receta en particular y se establece en el constructor de la página.
En segundo lugar, los controladores de eventos Clicked
para saveButton
y cancelButton
están totalmente .NET-ified (y sí, invento mis propias palabras con bastante frecuencia).
Digo que están .NET-ified porque la sintaxis para manejar ese evento no es nativa de Java ni de Objective-C. Cuando la aplicación se ejecuta en Android o iOS, el comportamiento será exactamente como un clic de Android o un TouchUpInside de iOS.
Y como puede ver, cada uno de esos controladores de eventos de clic está invocando funciones apropiadas que guardan la receta y descartan la página, o solo descartan la página.
Ahí está: ¡tenemos la interfaz de usuario inactiva para guardar las recetas desde ahora hasta el final de los tiempos!
CSS ¿Qué? O hacer que la aplicación sea bonita
Dejando lo mejor para el final: Xamarin.Forms 3.0 nos brinda, entre otras cosas, la capacidad de diseñar controles usando CSS.
El CSS de Xamarin.Forms no es 100 % a lo que puede estar acostumbrado desde el desarrollo web. Pero está lo suficientemente cerca como para que cualquiera que esté familiarizado con CSS se sienta como en casa. ¡Como yo en casa de la abuela!
Entonces, tomemos la página Detalles de la receta y refactorícela, de modo que use Hojas de estilo en cascada para configurar los elementos visuales en lugar de configurar todo directamente en línea en el XAML.
¡El primer paso es crear el documento CSS! En este caso se verá de la siguiente manera:
.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; }
En su mayor parte, parece CSS. Hay clases ahí. Hay un único selector para un tipo de clase, Image
. Y luego un grupo de setters de propiedad.
Algunos de esos establecedores de propiedades, como flex-wrap
o flex-basis
son específicos de Xamarin.Forms. En el futuro, el equipo prefijará aquellos con xf-
para seguir las prácticas estándar.
El siguiente paso será aplicarlo a los controles 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>
Así es como se veía antes.
En Xamarin.Forms, para hacer referencia al documento CSS, agregue un <StyleSheet Source="YOUR DOC PATH" />
. Luego puede hacer referencia a las clases en cada control a través de la propiedad StyleClass
.
Definitivamente limpia el XAML y también aclara la intención del control. Por ejemplo, ahora es bastante obvio lo que esos <BoxView StyleClass="spacer" />
están haciendo.
Y la Image
tiene un estilo propio porque es una Image
y la forma en que definimos el selector en el CSS.
Sin duda, CSS en Xamarin.Forms no está tan completamente implementado como su primo web, pero aún así es bastante bueno. Tiene selectores, clases, puede establecer propiedades y, por supuesto, ¡todo ese proceso en cascada!
Resumen
¡Tres pantallas, dos plataformas, un artículo y un sinfín de recetas guardadas! ¿Y sabes qué más? Puede crear aplicaciones con Xamarin.Forms para más de Android e iOS. ¡Puede construir plataformas UWP, macOS e incluso Samsung Tizen!
Xamarin.Forms es un kit de herramientas de interfaz de usuario que le permite crear aplicaciones escribiendo la interfaz de usuario una vez y renderizando la interfaz de usuario de forma nativa en las principales plataformas.
Para ello, proporciona un SDK que es una abstracción de los controles más utilizados en las plataformas. Además de la bondad de la interfaz de usuario, Xamarin.Forms también proporciona un marco MVVM con todas las funciones, un servicio de mensajería pub/sub, una API de animación y un servicio de dependencia.
Xamarin.Forms también le brinda los mismos beneficios de código que el desarrollo tradicional de Xamarin. Cualquier lógica de aplicación se comparte entre todas las plataformas. Y puede desarrollar todas sus aplicaciones con un solo IDE usando un solo idioma, ¡eso es genial!
¿Dónde seguir? Descargue el código fuente de esta aplicación Xamarin.Forms para probarla usted mismo. Luego, para obtener más información sobre Xamarin.Forms, incluida la capacidad de crear una aplicación dentro de su navegador, consulte este tutorial en línea.