[Xamarin UI Challenge] Flights UI

El reto

Volvemos a por un reto de interfaz de usuario con Xamarin.Forms. En esta ocasión, crear un efecto Pull To Refresh personalizado.

La UI a replicar

Los retos del ejemplo

Los retos para conseguir un Pull to Refresh personalizado son los siguientes:

  • Pull to Refresh: Contamos con el control RefreshView diseñado justamente para hacer pull to refresh. Sin embargo, el contenido al hacer refresco no es personalizable. Así que, para conseguir el resultado deseado vamos a utilizar un SwipeView. El contenido del SwipeView será el listado de vuelos mientras que usaremos un SwipeItemView (soporta una View como contenido) para añadir la animación personalizada.
  • La animación de refresco: Para la animación de refresco vamos a usar Lottie.
  • El listado de vuelos: El listado de vuelos se realizará usando CollectionView.

Listado de vuelos

El listado es de todos los elementos de la interfaz, la parte más sencilla.

<CollectionView
     ItemsSource="{Binding Booking}">
</CollectionView>

Mostramos un listado de vuelos resultados de una búsqueda. En la cabecera mostramos información básica como el número total de resultados. Podemos conseguir añadir la información encima de cada resultado usando la cabecera (Header) del CollectionView.

<CollectionView.Header>
     <Grid
          Padding="0, 12, 0, 0">
          <Label 
               Text="2 Search Results"
               Style="{StaticResource HeaderStyle}"/>
     </Grid>
</CollectionView.Header>

El resultado:

La cabecera del listado de vuelos

Cada resultado de trayecto puede tener múltiples vuelos. Vamos a usar Bindable Layout dentro de cada elemento del CollectionView.

<StackLayout
     BindableLayout.ItemsSource="{Binding Flights}"> 
</StackLayout>

De esta forma adaptaremos cada celda de forma sencilla a mostrar el total de paradas y trayectos de un resultado:

Detalles de cada vuelos

Sencillo, ¿verdad?.

Pull to Refresh personalizado

La clave del ejemplo, el efecto Pull to Refresh personalizado lo conseguimos usando el control SwipeView. SwipeView permite cuatro direcciones para hacer swipe: arriba, abajo, izquierda y derecha. En nuestro caso, queremos hacer pull desde la parte superior y por ello vamos a utilizar TopItems. Podemos usar:

  • SwipeItem: Acción contextual sencilla. Es fácil de configurar (color de fondo, texto e icono), pudiendo ejecutar un comando o evento.
  • SwipeItemView: Permite como contenido una View, por lo que las opciones de personalización son más altas.

¿Sabes que vamos a utilizar en este caso?. Probablemente has acertado, vamos a usar un SwipeItemView donde vamos a añadir nuestra animación personalizada.

<SwipeView
     x:Name="SwipeView"
     SwipeEnded="OnSwipeEnded">
     <!-- PULL TO REFRESH CONTENT -->
     <SwipeView.TopItems>
          <SwipeItemView>
               <Grid
                    HeightRequest="60">
                    <!-- CUSTOM ANIMATION -->
               </Grid>
          </SwipeItemView>
     </SwipeView.TopItems>
     <!-- CONTENT -->
     <Grid>
          <CollectionView
               ItemsSource="{Binding Booking}">
          </CollectionView>
     </Grid>
</SwipeView>

Pasamos a ver como definir la animación personalizada.

Animación usando Lottie

La librería Lottie fue creada por Airbnb inicialmente para iOS, Android y React Native. Sin embargo, gracias a la contrinución de Martijn van Dijk y otros miembros de la comuidad, tenemos soporte en Xamarin y Xamarin.Forms.

Para comenzar a trabajar con Lottie en Xamarin.Forms utilizaremos el paquete NuGet Aribnb.Xamarin.Forms.Lottie:

Install-Package Com.Airbnb.Xamarin.Forms.Lottie

Para completar el proceso de preparación de Lottie en nuestro proyecto Xamarin.Forms, necesitamos añadir la siguiente línea tras la inicialización de Xamarin.Forms en cada proyecto nativo:

AnimationViewRenderer.Init();

Para poder mostrar una animación de Lottie, debemos añadir el archivo JSON a cada proyecto nativo:

  • Android: En la carpeta Assets. El archivo debe tener como acción de compilación AndroidAsset.
  • iOS: En la carpeta Resources. El archivo debe tener como acción de compilación BundleResource.
  • UWP: En la carpeta Assets (o en la raíz), el archivo debe tener como acción de compilación Content.

Para mostrar la animación necesitamos un elemento visual que nos permita visualizar la animación además realizar una gestión de la misma. Este elemento visual es AnimationView.

<SwipeView.TopItems>
     <SwipeItemView>
          <Grid
               BackgroundColor="{StaticResource AccentColor}"
               HeightRequest="60">
               <lottie:AnimationView
                    Animation="pulltorefresh.json"
                    Loop="False" 
                    AutoPlay="True"
                    VerticalOptions="FillAndExpand"
                    HorizontalOptions="FillAndExpand"/>
          </Grid>
     </SwipeItemView>
</SwipeView.TopItems>

En nuestro ejemplo usamos esta gran animación creada por Lenny Miranda jr.

Animación de refresco

Obtener recursos

Un gran fuente de animaciones es LottieFiles. Puedes encontrar una gran variedad de animaciones listas para utilizar con opciones de búsqueda, etc.

LottieFiles

NOTA: Si usas animaciones de LottieFiles, recuerda otorgar crédito a sus creadores.

Con la definición del CollectionView, SwipeView y animación con Lottie, tenemos todo lo necesario. Veamos el resultado final!.

El resultado

Puedes encontrar el código en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Creando StateTriggers personalizados

StateTriggers

Recientemente hablábamos en el blog acerca de los StateTriggers añadidos en Xamarin.Forms. Los StateTriggers representan un conjunto de reglas que aplican VisualStates basados ciertas condiciones. Ahora, cada VisualState cuenta con una colección de StateTriggerBase que indican cuando el VisualState se debe aplicar. Si alguno de los triggers esta activo, el VisualState será aplicado.

Xamarin.Forms facilita ciertos StateTriggers para cubrir casos habituales como la orientación del dispositivos, cambios dinámicos en el tamaño de la ventana de la App, etc. Sin embargo, ¿no sería genial poder extender las posibilidades y crear StateTriggers personalizados?.

Creando StateTriggers personalizados

Podemos crear nuestros propios StateTriggers!. A continuación, vamos a aprender como crearlos. Para entender el concepto bien vamos a crear un trigger que nos permite actualizar la UI basándose en el estado de la conexión a internet.

Lo primero es comenzar creando una clase que here de StateTriggerBase:

public class NetworkConnectionStateTrigger : StateTriggerBase
{

}

Para aplicar un VisualState necesitamos establecer la propiedad IsActive a True. Podemos usar el método SetActive disponible en StateTriggerBase para ello. Necesitamos “algo” para poder gestionar que estados aplicar cuando hay conexión y cuando no, para ellos vamos a crear una BindableProperty:

public bool IsConnected
{
     get => (bool)GetValue(IsConnectedProperty);
     set => SetValue(IsConnectedProperty, value);
}

public static readonly BindableProperty IsConnectedProperty =
     BindableProperty.Create(nameof(IsConnected), typeof(bool), typeof(NetworkConnectionStateTrigger), false,
          propertyChanged: OnIsConnectedChanged);

Al cambiar la propiedad vamos a actualizar el valor de IsActive.

void UpdateState()
{
    var current = Connectivity.NetworkAccess;

    if (IsConnected)
         SetActive(current == NetworkAccess.Internet);
    else 
         SetActive(current != NetworkAccess.Internet);
}

Utilizamos las APIs disponibles para conocer el estado de la red disponibles en Xamarin.Essentials. Hasta este punto, tenemos el trigger casi completo (sencillo, ¿verdad?) pero, nos falta un detalle. Estará genial actualizar el valor de IsActive de forma automática al cambiar el estado de la red. Para ello, vamos a utilizar el evento ConnectivityChanged:

Connectivity.ConnectivityChanged += OnConnectivityChanged;

Todo preparado, vamos a ver como utilizar el StateTrigger creado.

Usar el StateTrigger creado

Lo primero que debemos hacer es declarar el namespace en XAML donde hemos creado el StateTrigger:

xmlns:statetriggers="clr-namespace:XamarinFormsStateTriggers"

Para usarlo:

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState 
                    x:Name="NoConnected">
                    <VisualState.StateTriggers> 
                        <statetriggers:NetworkConnectionStateTrigger IsConnected="False" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Red" />
                         <Setter TargetName="InfoLabel" Property="Label.Text" 
                              Value="No Internet connection available" />
                    </VisualState.Setters>
               </VisualState>
               <VisualState
                    x:Name="Connected">
                    <VisualState.StateTriggers>
                         <statetriggers:NetworkConnectionStateTrigger IsConnected="True" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Green" />
                    </VisualState.Setters>
               </VisualState>
          </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
     <Label
          x:Name="InfoLabel"
          Text="Connected"
          HorizontalOptions="Center"
          VerticalOptions="Center" />
</Grid>

Hemos creado dos VisualState, uno que aplicaremos cuando hay conexión, y otro cuando no. Usamos NetworkConnectionStateTrigger con la propiedad creada, IsConnected para automáticamente aplicar VisualStates en base a la conexión.

El resultado:

NetworkConnectionStateTrigger

Puedes encontrar el código en GitHub:

Ver GitHub

Personalmente, pienso que la posibilidad de crear StateTriggers personalizados cubre un hueco interesante facilitando en gran medida opciones anteriormente posibles. Además, una vez creado el StateTrigger es reutilizable!. Y a ti, ¿qué te parecen?. Recuerda, puedes dejar cualquier feedback en los comentarios de la entrada.

Más información

[Xamarin.Forms] StateTriggers

Introducción

En forma de Preview acaba de llegar en Xamarin.Forms mejoras en VisualStateManager con el soporte a StateTriggers.

StateTriggers

Los StateTriggers representan un conjunto de reglas que aplican VisualStates basados ciertas condiciones. Ahora, cada VisualState cuenta con una colección de StateTriggerBase que indican cuando el VisualState se debe aplicar. Si alguno de los triggers esta activo, el VisualState será aplicado.

NOTA: Si has desarrollado previamente para UWP, el concepto de StateTriggers te será familiar.

StateTriggers es una Preview por lo que para usarlo, recuerda usar la siguiente etiqueta experimental:

Xamarin.Forms.Forms.SetFlags("StateTriggers_Experimental");

AdaptiveTrigger

Este trigger es muy versátil. Permite definir reglas en XAML basadas en el tamaño de la pantalla. Veamos un ejemplo:

Cuenta con dos posibles valores:

  • MinWindowHeight
  • MinWindowWidth

Veamos un ejemplo sencillo:

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState x:Name="Narrow">
                    <VisualState.StateTriggers>
                         <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                   <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Blue" />
                   </VisualState.Setters>
              </VisualState>
              <VisualState x:Name="Medium">
                   <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="720" />
                   </VisualState.StateTriggers>
                   <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Red" />
                   </VisualState.Setters>
               </VisualState>
               <VisualState x:Name="Large">
                    <VisualState.StateTriggers>
                         <AdaptiveTrigger MinWindowWidth="1000" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Green" />
                    </VisualState.Setters>
               </VisualState>
          </VisualStateGroup>
     </VisualStateManager.VisualStateGroups> 
</Grid>

El resultado:

AdaptiveTrigger

El color de fondo del Grid será azul. Si el ancho de la ventana es mayor que 720 pasará (automáticamente) a ser rojo. Por último, el color cambiará a verde si el ancho es mayor que 1000.

CompareStateTrigger

CompareStateTrigger activará el trigger si el valor de Property es igual a Value.

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState x:Name="Checked">
                    <VisualState.StateTriggers>
                         <CompareStateTrigger Property="{Binding IsChecked, Source={x:Reference CheckBox}}" Value="True" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Green" />
                    </VisualState.Setters>
               </VisualState>
               <VisualState x:Name="UnChecked">
                    <VisualState.StateTriggers>
                         <CompareStateTrigger Property="{Binding IsChecked, Source={x:Reference CheckBox}}" Value="False" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Red" />
                    </VisualState.Setters>
               </VisualState>
          </VisualStateGroup> 
     </VisualStateManager.VisualStateGroups> 
     <CheckBox
           x:Name="CheckBox"/>
</Grid>

Veamos el resultado:

CompareStateTrigger

Como podemos ver, estamos modificando el BackgroundColor del Grid en base a si el CheckBox esta marcado o no. Si el ChechBox esta marcado, el color de fondo es verde, en otro caso será rojo.

Se puede utilizar enlace a datos por lo que la comparación puede ser con una propiedad de la ViewModel, una propiedad de un elemento visual, etc. abriendo una enorme variedad de posibilidades de forma muy sencilla.

DeviceStateTrigger

Un StateTrigger sencillo que nos permite activar VisualStates en base al dispositivo donde se ejecuta la App:

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState
                    x:Name="Android">
                    <VisualState.StateTriggers>
                         <DeviceStateTrigger Device="Android" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Blue" />
                    </VisualState.Setters>
               </VisualState>
               <VisualState
                    x:Name="iOS">
                    <VisualState.StateTriggers>
                         <DeviceStateTrigger Device="iOS" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Red" />
                    </VisualState.Setters>
               </VisualState>
          </VisualStateGroup> 
     </VisualStateManager.VisualStateGroups> 
</Grid>

Si ejecutamos en Android:

DeviceStateTrigger en Android

Y si la App se ejecuta en iOS:

DeviceStateTrigger en iOS

Sencillo, ¿verdad?.

OrientationStateTrigger

Este StateTrigger cuenta con la propiedad Orientation que nos permite aplicar VisualStates en base a la orientación del dispositivo:

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState
                    x:Name="Landscape">
                    <VisualState.StateTriggers>
                         <OrientationStateTrigger Orientation="Landscape" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Blue" />
                    </VisualState.Setters>
               </VisualState>
               <VisualState
                    x:Name="Portrait">
                    <VisualState.StateTriggers>
                         <OrientationStateTrigger Orientation="Portrait" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Red" />
                    </VisualState.Setters>
                </VisualState>
           </VisualStateGroup>
      </VisualStateManager.VisualStateGroups> 
</Grid>

El resultado:

OrientationStateTrigger

Sin necesidad de suscribirse a eventos o lógica extra.

StateTriggers y Surface Duo/Neo (Dual Screen)

Xamarin.Forms le da soporte a Surface Duo y Surface Neo (dispositivos con dos pantallas) con el paquete Xamarin.Forms.DualScreen principalmente gracias a TwoPaneView.

Sin embargo, en la librería Xamarin.Forms.DualScreen también tenemos StateTriggers específicos a la hora de gestionar la UI al trabajar con dos pantallas.

El SpanModeStateTrigger cuenta con la propiedad SpanMode que nos permite ajustar la UI usando VisualStates en base a si estamos usando una única pantalla, etc.

<Grid>
     <VisualStateManager.VisualStateGroups>
          <VisualStateGroup>
               <VisualState x:Name="NotSpanned">
                    <VisualState.StateTriggers>
                         <dualScreen:SpanModeStateTrigger SpanMode="SinglePane"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Red" />
                    </VisualState.Setters>
               </VisualState>
               <VisualState x:Name="Spanned">
                    <VisualState.StateTriggers>
                         <dualScreen:SpanModeStateTrigger SpanMode="Wide" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                         <Setter Property="BackgroundColor" Value="Green" />
                    </VisualState.Setters>
               </VisualState>
          </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
</Grid>

Usando una única pantalla:

Utilizando una pantalla

Al usar dos:

Utilizando las dos pantallas

Han llegado StateTriggers a Xamarin.Forms y pronto llegarán más opciones relacionadas. En algunos casos, hay StateTriggers que nos permiten hacer algo que ya podíamos de forma sencilla (por ejemplo, ajustes en la UI al cambiar la orientación) mientras que en otros casos, abre muchas posibilidades. ¿Qué te parece esta nueva funcionalidad?. Recuerda, puedes dejar cualquier feedback en los comentarios de la entrada.

Más información

[Xamarin.Forms] Embedded fonts

Introducción

Usar fuentes personalizadas consigue ayudar a trasmitir la imagen y rasgos característicos de una App móvil. Por supuesto, es posible utilizar fuentes personalizadas en Xamarin.Forms. Sin embargo, con la versión 4.5 nos llega una nueva posibilidad a la otra de trabajar con fuentes.

Embedded fonts

Nos llega una nueva opción a la hora de utilizar fuentes personalizadas, aún mas sencilla.

Hasta ahora, añadíamos los recursos de las fuentes en cada proyecto nativo en las correspondientes carpetas (carpeta Assets en Android o Resources en iOS).

Ahora, para usar fuentes, solo se necesita añadir la fuente como recurso incrustado en la librería compartida, en lugar de agregarlas a múltiples plataformas, cada una con sus propias reglas de implementación.

Recurso incrustado

A continuación, en el archivo AssemblyInfo.cs agregue:

[assembly: ExportFont("Montserrat-Regular.ttf")]

Y posteriormente, podemos usar la fuente:

<Label 
     Text="Montserrat-Regular (EmbeddedFont)"
     Font="Montserrat-Bold" />

El cambio principal radica en poder añadir los recursos de las fuentes una única vez en el proyecto compartido simplificando el uso. ¿Qué te parece este cambio?. Recuerda, cualquier duda o pregunta es bienvenida en los comentarios de la entrada.

Más información

[Xamarin.Forms] Establecer el directorio de recursos en Apps UWP

Diferentes recursos por plataformas

Usando Xamarin.Forms desarrollamos (habitualmente) aplicaciones para diferentes plataformas compartiendo grandes cantidades de código. La clase Device contiene una serie de propiedades y métodos para ayudar a personalizar la UI y la funcionalidad para cada plataforma.

Por ejemplo:

<StackLayout>
     <StackLayout.Margin>
          <OnPlatform x:TypeArguments="Thickness">
               <On Platform="iOS" Value="0,12,0,0" />
               <On Platform="Android, UWP" Value="0,0,0,12" />
          </OnPlatform>
     </StackLayout.Margin>
</StackLayout>

En el ejemplo anterior estamos personalizando la interfaz de usuario usando diferentes márgenes en iOS y en Android.

Cuando lo unico diferente es la ruta…

Sin embargo, un caso bastante habitual donde acabamos usando OnPlatform es a la hora de trabajar con imágenes.

<Image>
     <Image.Source>
          <OnPlatform x:TypeArguments="ImageSource">
               <On Platform="Android, iOS" Value="icon.png"/>
               <On Platform="UWP" Value="Assets/icon.png"/>
          </OnPlatform>
     </Image.Source>
</Image>

Esto sucede porque mientras que en Android e iOS las carpetas para añadir recursos (Assets, Resources) permiten escribir directamente el nombre del archivo, en el caso de UWP, las imágenes se encuentran dentro de la carpeta Assets del proyecto UWP.

¿Y si podemos simplificar estos casos?.

Nuevo Platform Specific para cubrir estos casos

Desde la versión 4.5.0.142-pre1, a nivel de aplicación o página, podemos usar el siguiente Platform Specific para UWP:

Application.Current.On<Windows>().SetImageDirectory("Assets");

Donde pasamos en el método, el nombre del directorio que deseamos que Xamarin.Forms use para buscar recursos por nosotros.

Gracias al Platform Specific anterior, podemos pasar de:

<Image>
     <Image.Source>
          <OnPlatform x:TypeArguments="ImageSource">
               <On Platform="Android, iOS" Value="icon.png"/>
               <On Platform="UWP" Value="Assets/icon.png"/>
          </OnPlatform>
     </Image.Source>
</Image>

A lo siguiente:

<Image Source="icon.png"/>

Sencillo, ¿verdad?.

Más información

Un vistazo a Mobile Blazor Bindings

Introducción

Recientemente se anunciaban los Mobile Blazor Bindings. Hablamos de un proyecto experimental que consiste en añadir bindings para poder desarrollar aplicaciones móviles nativas para Android e iOS usando C# y .NET utilizando el modelo de desarrollo de Blazor y la síntaxis Razor para definir la UI de la aplicación. Los componentes de UI se basan en Xamarin.Forms. Hasta ahora podíamos crear la interfaz de usuario en aplicaciones Xamarin.Forms usando XAML o C#. Gracias estos nuevos bindings se abre una nueva posibilidad.

Pero…¿por que crear estos bindings con Blazor?.

Hay un grupo de desarrolladores Xamarin.Forms que vienen del desarrollo web y han trasmitido que se sentirían más cómodos usando conocimientos más relacionados con su área. El objetivo principal de estos bindings es facilitar el desarrollo de aplicaciones móviles a desarrolladores web con un “estilo Blazor” utilizando sintaxis Razor.

Mobile Blazor Bindings

Los Bindings de Blazor para desarrollo móvil constan de dos librerías principales:

  • Microsoft.MobileBlazorBindings.Core: Esta libreria contiene implementaciones de un adaptador que mapea entre elementos de UI nativos y componentes de Blazor.
  • Microsoft.MobileBlazorBindings: una librería que añade bindings específicos para elementos de Xamarin.Forms.

Arquitectura

Puedes ver la introducción realizada por Eilon Lipton a continuación:

Creando una primera App

Antes que nada, necesitarás:

  • El SDK de .NET Core 3.0 o 3.1.
  • Visual Studio (Windows o Mac) con las herramientas para desarrollar con Xamarin y ASP.NET instaladas.

Contando con lo anterior, estamos casi preparados para arrancar!. Vamos a crear un proyecto de inicio usando la linea de comandos. Abre la linea de comandos y escribe:

dotnet new -i Microsoft.MobileBlazorBindings.Templates::0.1.173-beta

Esto descargará las plantillas necesarias. A continuación:

dotnet new mobileblazorbindings -o MyMobileBlazorBindingsApp

Esto creará una solución con varios proyectos.

  • Un proyecto con el código compartido (UI y lógica).
  • Un proyecto para Android.
  • Un proyecto para iOS.

El código para un “Hola Mundo” sería algo como lo siguiente:

<StackLayout>
     <Label FontSize="30"
          Text="@("You pressed " + count + " times")" />
     <Button Text="+1"
          OnClick="@HandleClick" />
</StackLayout>

@code {
     int count;

     void HandleClick()
     {
          count++;
     }
}

Con el proyecto creado, basta con compilar y lanzar en algun emulador o dispositivo:

Primera App

Más ejemplos

En el repositorio oficial hay varios ejemplos con una mezcla entre muestra de diferentes componentes de UI y acceso a la plataforma nativa (Text to Speech, etc), uso de SQLite, etc.

Estado actual

Las aplicaciones creadas con Mobile Blazor Bindings usan componentes de UI. Estos componentes usan sintaxis Razor y se basan en controles de Xamarin.Forms.

El estado actual implementado es el siguiente:

  1. Páginas
    • ContentPage
    • MasterDetailPage
    • Page
    • TabbedPage
    • TemplatedPage
  2. Layouts
    • ContentView
    • Frame
    • Grid
    • ScrollView
    • StackLayout
  3. Views
    • Button
    • ActivityIndicator
    • Image
    • Entry
    • Label
    • Stepper
    • Switch
  4. Otros componentes
    • Application
    • BaseMenuItem
    • FormattedString
    • GestureElement
    • MenuItem
    • Shell (including ShellContent, ShellGroupItem, ShellItem, FlyoutItem, TabBar, ShellSection, Tab)
    • Span

Roadmap

Al ser un proyecto experimental, hay varias areas importantes donde continuar evolucionando:

  • Añadir soporte a Hot Reload.
  • Añadir más wrappers de elementos Xamarin.Forms.
  • Mejorar el soporte de Shell.
  • Soportar navegación por URL.
  • Etc.

Feedback

Al ser un proyecto experimental, todo el feedback es bienvenido!. Puedes ver un listado de issues conocidas en este enlace, aunque si tienes feedback o alguna issue concreta no registrada puedes hacerlo en el repositorio en GitHub.

Más información

[Xamarin.Forms] IndicatorView

Introducción

Recientemente recibíamos en Xamarin.Forms un nuevo CarouselView. El CarouselView permite mostrar una colección donde se pueden desplazar los elementos haciendo deslizamientos (gestos de Swipe). A pesar de contar con diferentes opciones para gestionar el layout, orientación, espaciado, etc. una de las peticiones más habituales a la hora de trabajar con el nuevo Carousel es poder mostrar un indicador con el número de elementos y la posición.

Indicador de posición

Llega en Xamarin.Forms 4.4 un nuevo control, IndicatorView, que tiene como objetivo permitir mostrar el número de elementos y la posición del elemento actual.

IndicatorView

IndicatorView sale en forma de Preview y para poder usarlo en estos momentos es necesario usar la etiqueta IndicatorView_Preview.

Xamarin.Forms.Forms.SetFlags("IndicatorView_Experimental");

IndicatorView nos permite mostrar un indicador de posición de diferentes formas, bien usando una forma sencilla o incluso usando una plantilla personalizada. Normalmente, el indicador va asociado a un Carousel:

<CarouselView 
     x:Name="Carousel" 
     ItemsSource="{Binding Items}">
     <CarouselView.ItemTemplate>
          <DataTemplate>
               <Frame 
                    BackgroundColor="{Binding Color}">
                    <Grid 
                         HorizontalOptions="Center" 
                         VerticalOptions="Center">
                         <Label 
                              Text="{Binding Name}" 
                              FontSize="25" />
                    </Grid>
               </Frame>
          </DataTemplate>
     </CarouselView.ItemTemplate>
</CarouselView>

A continaución, vamos a ver todas las propiedades básicas que definen la apariencia de IndicatorView.

Formas básicas

La forma más sencilla de usar IndicatorView es la siguiente:

<IndicatorView 
     ItemsSourceBy="Carousel" 
     IndicatorsShape="Circle" 
     IndicatorColor="Gray" 
     SelectedIndicatorColor="White" 
     HorizontalOptions="Center" />

Vaya, hemos utilizado varias propiedades. Vamos a revisar el conjunto de propiedades usadas:

  • ItemsSourceBy: El control muestra el conjunto de elementos disponibles en una colección y el elemento seleccionado (o actual) correspondiente a otro control (normalmente, un CarouselView). Esta propiedad permite indicar el control que será fuente de datos.
  • IndicatorShape: Permite definir la apariencia visual de la forma del indicador. Por ahora se soportan dos opciones, Circle o Square.
  • IndicatorColor: Color que se aplicará al fondo de todas los elementos que componen el IndicatorView.
  • SelectedIndicatorColor: Color aplicado al elemento seleccionado o actual.

El resultado:

IndicatorView usando formas básicas

Usando plantillas

¿Y qué ocurre si no nos encaja el uso de una forma básica (circulo o cuadrado)?. IndicatorView también cuenta con la propiedad IndicatorTemplate  que permite usando cualquier contenido personalizado:

<IndicatorView 
     x:Name="IndicatorsForms" 
     ItemsSourceBy="Carousel" 
     IndicatorColor="Transparent" 
     SelectedIndicatorColor="Gray" 
     HorizontalOptions="Center" 
     Visual="Forms">
     <IndicatorView.IndicatorTemplate>
          <DataTemplate>
               <Image>
                    <Image.Source>
                         <FontImageSource 
                              Glyph="{StaticResource Indicator}" 
                              FontFamily="{StaticResource IonicsFontFamily}" 
                              Color="Pink" />
                    </Image.Source>
               </Image>
          </DataTemplate>
     </IndicatorView.IndicatorTemplate>
</IndicatorView>

El resultado:

Usando plantillas

IndicatorView es una opción interesante que llega como un complemente perfecto ante determinadas necesidades relacionadas con colecciones (principalmente CarouselView). ¿Qué te parece el control?. Recuerda que cualquier duda o pregunta es bienvenida en los comentarios!.

Más información

[Xamarin.Forms] Un primer vistazo a SwipeView

Opciones contextuales

Los dispositivos móviles han cambiado la forma en la que el usuario interactua con el contenido de la aplicación. Un ejemplo de ello, es el uso de opciones contextuales. Hablamos de las opciones contextuales a las que podemos acceder realizando un gesto de deslizamiento (swipe).

Opciones contextuales

En Xamarin.Forms 4.4 nos llega SwipeView, un nuevo control con el objetivo de cubrir la funcionalidad relacionada con opcones contextuales. Disponible para Android, iOS y UWP (pronto también en Tizen).

SwipeView

SwipeView permite tener una colección de elementos a los que acceder haciendo un gesto de deslizamiento (Swipe).

SwipeView

Izquierda, derecha, arriba o abajo

El uso habitual de estas opciones contextuales va asociada a un listado de elementos, pero el control SwipeView puede usarse en cualquier contexto. Podemos usar SwipeView junto con BindableLayouts, ListView, CollectionView, CarouselView o incluso sin ninguna colección.

Además, se permite hacer el swipe hacia cualquier dirección:

  • Arriba
  • Abajo
  • Izquierda
  • Derecha

Para definir la dircción del swipe, SwipeView permite definir diferentes colecciones de SwipeItems:

  • TopItems
  • BottomItems
  • LeftItems
  • RightItems

Veamos un ejemplo:

<SwipeView>
     <SwipeView.BottomItems>
          <SwipeItems
               Mode="Execute">
               <SwipeItem 
                    Text="Delete"
                    Icon="coffee.png"
                    BackgroundColor="Red"
                    Invoked="OnInvoked"/>
          </SwipeItems>
     </SwipeView.BottomItems>
     <SwipeView.Content>
          <Grid
               HeightRequest="60"
               WidthRequest="300"
               BackgroundColor="LightGray">
               <Label
                    HorizontalOptions="Center"
                    VerticalOptions="Center"
                    Text="Swipe Up (Execute)"/>
    </Grid>
    </SwipeView.Content>
</SwipeView>

Swipe en cualquier dirección

SwipeItem o SwipeItemView

La colección de SwipeItems contiene uno o N SwipeItem. Contamos con dos tipos:

SwipeItem

Es un elemento básicos que permite definir:

  • Text: El texto a mostrar debajo del icono.
  • Icon: Una propiedad de tipo ImageSource (se soportan imagenes desde recursos, URL, etc.).
  • BackgroundColor: Define el color de fondo.

SwipeItemView

Si quieres mostrar algo con más opciones de personalización, SwipeItemView permite definir una View, es decir, se puede crear el contenido de forma personalizada con una composición de elementos.

SwipeItemView es idóneo si se quiere una mayor personalización (contenido personalizado, bordes redondeados, etc.) como por ejemplo:

Usando SwipeItemView

Ambas opciones, cuentan tanto con comando (pensando en MVVM) como con eventos.

  • Command: Comando a ejecutar.
  • CommandParameter: Es el parámetro (opcional) que podemos utilizar con el comando.
  • Invoked: Además de lanzar el comando, se lanza este evento.

Diferentes modos

Se cuentan con dos modos a la hora de trabajar con SwipeItems:

  • Reveal: En el modo mostrar, se desliza para abrir un menú de uno o varios comandos y se debe pulsar explícitamente un comando para ejecutarlo.
  • Execute: En el modo ejecutar, si se suelta el elemento que se está deslizando antes de pasar un umbral, el menú se cierra y el comando no se ejecuta. Si el usuario desliza el dedo más allá del umbral y luego lo suelta, el comando se ejecuta inmediatamente.

SwipeBehaviorOnInvoked

Se puede tener control sobre lo que ocurre al pulsar un elemento gracias a la propiedad SwipeBehaviorOnInvoked. Las opciones disponibles son:

  • Auto.
  • Close: Cuando se invoque el elemento, el SwipeView siempre se cerrará y volverá al estado normal, independientemente del modo.
  • RemainOpen: Cuando se invoque el elemento, el SwipeView siempre seguirá abierto, independientemente del modo.

Ejemplo:

leftSwipeItems.SwipeBehaviorOnInvoked = SwipeBehaviorOnInvoked.Auto;

Veámoslo en funcionamiento:

SwipeBehaviorOnInvoked

Eventos

Contamos con diferentes eventos que nos permiten controlar el estado del SwipeView:

  • SwipeStarted: Se lanza justo al iniciar el deslizamiento. Ofrece la dirección del swipe como información.
  • SwipeChanging: Se lanza cada vez que el deslizamiento cambia. Además de indicar la dirección, permite obtener el offset.
  • SwipeEnded: Es similar a SwipeStarted pero este se lanza al terminar el deslizamiento.

Eventos

¿Controlar la transición?

Por defecto, se realiza una transición de revelación de cada SwipeItem. Es decir, se hace un deslizamiento del contenido mientras que el conjunto de SwipeItems permanece fijo.

¿Y si podemos elegir entre diferentes opciones?.

Podemos hacerlo gracias a un Platform Specific en Android e iOS.

leftSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);

Contamos con dos opciones:

  • Reveal
  • Drag

A continuación, puedes ver las diferencias:

Platform Specific para establecer la transición

Cerrar programáticamente el SwipeView

En ocasiones, puede ser necesario cerrar el SwipeVIew programáticamente. Esto se puede conseguir de forma sencilla utilizando el método Close:

swipeView.Close();

El resultado:

Cerrar

Un detalle importante!

SwipeView instancia y liberar recursos al abrirse o cerrarse. La ventaja de esto es que, si tenemos un listado de 1000 elementos haciendo uso de SwipeView sin tener ninguno abierto, tendremos la misma jerarquía beneficiando el rendimiento.

Llegamos al final de la introducción al SwipeView. Usaremos este control en algun GoodLooking UI sample pronto (con su correspondiente artículo!). Sin embargo, ¿qué te parece esta nueva opción?. Cualquier feedback es bienvenido en los comentarios del artículo.

Más información

[Xamarin.Forms] Soporte a Gifs

Introducción

Desde los inicios de Xamarin.Forms se contaba con soporte para diferentes formatos de imagen. Con la llegada de nuevas versiones llegaron nuevas opciones como FontImageSource.

Con Xamarin.Forms 4.4 llega el soporte al formato gif y animaciones.

Soporte al formato gif

El uso de gif en Xamarin.Forms no implica grandes cambios en API o nuevos conceptos a aprender. Al igual que usamos la propiedad Source de Image con diferentes formatos de imagen (png, jpg, etc.), podemos usar gifs:

var gifImage = new Image
{
     Source = "awesome.gif"
};

NOTA: La funcionalidad completa relacionada con gifs en Android esta incluido usando FastRenderers (usados hoy día por defecto). Sin usar Fast Renderers (UseLegacyRenderers), el gif se renderizará pero la animación no funcionará.

Soporte a gifs

La novedad incluida relacionada con gifs viene a permitir controlar la animación del mismo. Contamos con la propiedad IsAnimationPlaying de tipo bool para indicar si el gif lanzará o no su animación.

gifImage.IsAnimationPlaying = true;

Sencillo, ¿verdad?.

¿Qué te parece el soporte al formato gif?. Recuerda, cualquier tipo de feedback es bienvenido en los comentarios de la entrada.

Más información

[Material] Monkey Conf 2019

El evento

En España tenemos una gran comunidad Xamarin y con la idea de contar con un evento de desarrollo relacionado con Xamarin de gran tamaño, el año pasado nacía la Monkey Conf. Ante el buen recibimiento, no podía más que hacer lo posible para volver este año con más contenido, mayor tamaño y más sorpresas.

El pasado 30 de Noviembre, celebramos la Monkey Conf 2019. A continuación, una pequeña galería del evento:

Fue un día lleno de sesiones técnicas de calidad. Es increíble ver sesiones donde se dan consejos de distribución y venta de apps basadas en la experiencia real con consejos de arquitectura de código. Esto demuestra el nivel y madurez de la comunidad Xamarin. Desde el inicio hasta el final, donde no podía faltar “el preguntón” (gracias a MFractor y SyncFusion por facilitarnos licencias) el día fluyo de forma natural, sin problemas. Muchas gracias a sponsors, asistentes y ponentes por ello!.

El material

Vamos al material. Participé en el evento con una sesión repasando lo que ha llegado en Xamarin.Forms en este año, así como revisando que llegará próximamente.

Al final, tuvimos 11 sesiones técnicas, 3 mesas redondas, 1 sesión de preguntas y respuestas además de 1 taller. Un día repleto de viejos y nuevos amigos, de compartir conocimiento, de enseñar y aprender. Un día rodeado de gente que llegaba desde Cataluña, Canarias, Valencia, Andalucía…

Solo puedo decir una cosa: Gracias!. Quiero terminar recordando que anunciamos la Monkey Conf 2020, así que, ¿nos vemos allí?.

Nos vemos en la Monkey Conf 2020!