[Xamarin.Forms UI Challenge] Art News, transiciones entre páginas

Introducción

Volvemos a por un reto de interfaz de usuario con Xamarin.Forms. En este artículo, vamos a tomar como referencia un diseño de Dribbble (por Shirley Yao), que intentaremos replicar con paso a paso.

Art News

Vamos a intentar replicar la UI del diseño paso a paso en Xamarin.Forms.

Los retos del ejemplo

Vamos a replicar dos pantallas con algunos retos, pero la clave del ejemplo es la transición de elementos compartidos entre las dos páginas.

  • Listado horizontal: La llegada de CollectionView es no solo una mejora en el rendimiento a la hora de trabajar con colecciones, también con diferentes Layouts (listados horizontales, GridViews, etc.). En este ejemplo, el número de elementos en el listado horizontal es bajo, por lo que podemos también hacer uso de un sencillo StackLayout y Bindable Layout.
  • Grid de fotos en los detalles: En caso de mostrar un número limitado o concreto de fotos en la galería mostrada en la página de detalles, podría usar un Layout. Probablemente un Grid o FlexLayout. Sin embargo, en caso de no ser limitado, CollectionView nos permite mostrar un número indeterminado de fotos en dos columnas de forma sencilla.
  • Transición entre páginas: Por defecto, no tenemos soporte a transiciones de páginas en Xamarin.Forms. Contamos con dos tipos de transiciones diferentes. Por un lado, las transiciones tradiciones que implican una animación de toda la página al entrar o salir. Por otro lado, tenemos las conocidas como transcisiones de elementos compartidos. En muchas ocasiones, tenemos un elemento visual compartido entre dos páginas (por ejemplo, una imagen) para trasmitir una sensación de fluidez y continuidad. En este ejemplo, veremos ambas opciones. Es necesario crear un Custom Renderer de la NavigationPage para conseguir el objetivo. Por suerte, no partimos de cero, utilizaremos Xamarin.Plugin.SharedTransitions.
  • Animaciones: Necesitamos una sencilla animación de Fade In y translación desde la parte inferior hacia la superior al navegar a la página de detalles (además de la transición). Xamarin.Forms cuenta con una completa Api de animaciones. En el ejemplo usaremos Xamanimation que nos ofrece animaciones prepadas, Storyboard y la posibilidad de usarlo todo desde XAML.

Listado horizontal

Para crear el listado horizontal utilizaremos Bindable Layouts.

<ScrollView 
     Orientation="Horizontal"
     HorizontalScrollBarVisibility="Default"
     VerticalScrollBarVisibility="Never">
     <StackLayout 
          x:Name="Highlights"
          Padding="20, 0, 0, 36"
          Orientation="Horizontal"
          BindableLayout.ItemsSource="{Binding Author.Highlights}">
          <BindableLayout.ItemTemplate>
               <DataTemplate>
                     <Grid
                          x:Name="HighlightTemplate"
                          RowSpacing="0"
                          Style="{StaticResource HighlightStyle}">
                          ...
                    </Grid>
               </DataTemplate>
          </BindableLayout.ItemTemplate>
     </StackLayout>
</ScrollView>

Usamos un scroll horizontal (sin mostrar la barra de scroll vertical) con un StackLayout apilando los elementos horizontalmente. La clave es el uso de las propiedades BindableLayout.ItemsSource y BindableLayout.ItemTemplate.

El resultado:

Listado horizontal

NOTA: En caso de contar con un número de elementos elevado, recuerda que no tenemos virtualización, etc. al usar Bindable Layouts. En dicho caso, es más recomendable utilizar CollectionView.

Grid de fotos en los detalles

El Layout de fotos se puede conseguir de diferentes formas. Probablemente, y ante un número determinado de elementos (un número bajo de elementos) se podría usar un Grid o FlexLayout con Bindable Layouts. Sin embargo, en este caso se ha utilizado CollectionView.

<CollectionView
     Grid.Row="1"
     ItemsSource="{Binding ArtItem.Related}"
     SelectionMode="None"
     InputTransparent="True">
     <CollectionView.ItemsLayout>
           <GridItemsLayout 
                Orientation="Vertical" 
                Span="2"/>
     </CollectionView.ItemsLayout>
     <CollectionView.ItemTemplate>
          <DataTemplate>
               <templates:RelatedContentTemplate />
          </DataTemplate>
     </CollectionView.ItemTemplate>
</CollectionView>

Para mostrar las dos columnas, utilizamos la propiedad ItemsLayout con GridItemsLayout utilizando la propiedad Span para indicar el número de columnas deseadas.

El resultado:

Grid de contenido relacionado

Sencillo, ¿verdad?. La llegada de BindableLayouts y de CollectionView nos permite conseguir resultados que hasta ahora requerían la creación de Custom Controls o Custom Renderers.

Transiciones entre páginas

Llegamos al “plato fuerte” del ejemplo, las transiciones entre páginas. Vamos a utilizar una versión modificada (al momento de escribir este artículo quedan algunas Pull Request pendientea por mergear, aunque los cambios de este ejemplo acabarán estando probablemente en la librería) de Xamarin.Plugin.SharedTransitions.

La idea de la librería es:

  • Custom Renderer de NavigationPage donde permitir las transiciones tradicionales (animación de entrada y salida de una página).
  • Effects que poder aplicar a elementos de la UI para permitir aplicar transiciones de elementos compartidos.

En cualquier página (ContentPage), podemos indicar la transición a utilizar de forma sencilla utilizando el método SetBackgroundAnimation:

SharedTransitionNavigationPage.SetBackgroundAnimation(this, BackgroundAnimation.SlideFromLeft);
SharedTransitionNavigationPage.SetSharedTransitionDuration(this, 500);

Entre las opciones disponibles:

  • Fade
  • Flip
  • SlideFromLeft
  • SlideFromRight
  • SlideFromTop
  • SlideFromBottom

¿Y las transiciones de elementos conectados?

Vamos a ver como utilizarlas. Comenzando añadiendo el namespace XAML necesario:

xmlns:sharedTransitions="clr-namespace:Plugin.SharedTransitions;assembly=Plugin.SharedTransitions"

En casos básicos, por ejemplo, conectar dos elementos individuales, por ejemplo un botón en dos páginas diferentes, bastará con utilizar la propiedad Tag disponible en la clase Transition.

En caso de querer hacer transiciones entre elementos de una colección como es nuestro caso (imagen correspondiente a una plantilla usada en un Bindable Layout), necesitamos utilizar además de Tag, la propiedad TagGroup.

sharedTransitions:Transition.TagGroup="1"
sharedTransitions:Transition.Tag="{Binding Number}"

NOTA: Cada elemento debe tener un Tag único.

En la página de destino, volvemos a aplicar el mismo Tag utilizado en la página anterior.

sharedTransitions:Transition.Tag="{Binding ArtItem.Number}"

NOTA: No es necesario utilizar TagGroup en la página de destino.

¿Limitaciones?

  • De momento, entre las transiciones tradicionales soportadas se incluyen las opciones vistas previamente. Proximamente espero añadir alguna opción más (Scale, etc.).
  • Las transiciones de elementos compartidos funcionan con: Label, Image, Button. Próximamente se añadirá soporte a más elementos.
  • Funciona utilizando una NavigationPage. No tiene integración con Shell por ahora.

Animaciones

Llegamos al detalle final del ejemplo. Aunque al navegar a la página de detalles la transición realizada con la imagen que se situará como cabecera conseguirá ya un efecto de fluidez y continuidad elevado, también contamos con una animación del resto del contenido.

Xamarin.Forms cuenta con una API de animaciones completa y sencilla de utilizar. Sin embargo, vamos a conseguir el efecto buscado de forma aún más sencilla directamente desde XAML utilizando Xamanimation.

Para utilizar la librería, comenzamos añadiendo el namespace en la página de detalles:

xmlns:xamanimation="clr-namespace:Xamanimation;assembly=Xamanimation"

A continuación, vamos a crear un Storyboard:

<xamanimation:StoryBoard
     x:Key="ArtItemContentAnimation"
     Target="{x:Reference ArtItemContent}">
     <xamanimation:TranslateToAnimation TranslateY="0" Duration="300"/>
     <xamanimation:FadeInAnimation />
</xamanimation:StoryBoard>

El Storyboard nos permite realizar una animación más compleja, compuesta por otras animaciones. Vamos a hacer una animación de translación hacia arriba además de hacer animar la opacidad del contenido (de 0 a 1).

¿Y cómo se lanza la animación?

Utilizamos un Trigger, para lanzar la animación en el evento Appearing de la página:

<ContentPage.Triggers>
     <EventTrigger Event="Appearing">
          <xamanimation:BeginAnimation 
               Animation="{StaticResource ArtItemContentAnimation}" />
     </EventTrigger>
</ContentPage.Triggers>

El resultado final:

El resultado

¿Qué te parece?

En cuanto al ejemplo, esta disponible en GitHub:

Ver GitHub

Llegamos hasta aquí. Estamos ante un UI Challenge donde el mayor punto de interés recae en las transiciones. Espero que te haya resultado interesante. Pronto más y mejor. Recuerda, cualquier comentario es bienvenida en el artículo!.

Más información

Anuncios

[Xamarin.Forms UI Challenge] TimelinePulse

Introducción

Según evoluciona de Xamarin.Forms, llegan más y más opciones que simplifican la creación de diferentes elementos de la interfaz de usuario.

En el estado actual de Xamarin.Forms se pueden conseguir aplicaciones nativas de gran escala, con interfaces cuidadas y con alta integración con la plataforma. Hay que tener en cuenta el conjunto de Custom Renderers (código específico en cada plataforma) necesario para lograrlo.

NOTA: La elección entre Xamarin Classic o Xamarin.Forms es importante. Es necesario evaluar la aplicación a desarrollar, el conjunto de características específicas de cada plataforma (que pueden requerir un Custom Renderer), etc. 

En este artículo, vamos a tomar como referencia un diseño de Dribbble (por Anton Aheichanka), que intentaremos replicar con Xamarin.Forms paso a paso.

Timeline Profile

TimelinePulse

Vamos a intentar replicar la UI del diseño paso a paso en Xamarin.Forms.

Los retos del ejemplo

Vamos a comenzar haciendo un análisis de la interfaz de usuario desglosando los elementos que la componen:

  • Barra de navegación: Muestra el título centrado (y con una fuente específica) además de la imagen del perfil del usuario para permitir navegar rápidamente al perfil. Gracias a la propiedad TitleView de la NavigationPage, podemos añadir contenido personalizado y conseguir el resultado facilmente.
  • Cabecera: La cabecera muestra información relacionada con la fecha. Sin embargo, No hay nada complejo en la misma. Aplicar una imagen de fondo, y utilizar un Layout (por ejemplo, un Grid) para posicionar la información de la fecha con textos utilizando una fuente específica.
  • El botón para añadir: Aquí es donde vamos a tener la parte más compleja del ejemplo. ¿Por qué?. Queremos aplicar un efecto de pulso para llamar la atención del usuario. Tenemos varias formas de conseguir el resultado. Entre ellas contamos con el uso las APIs de animación de Xamarin.Forms junto con el uso de imágenes; SkiaSharp o bien Custom Renderers.
  • El listado: El listado cuenta con elementos que se pueden conseguir definiendo una celda personalizada. En la celda, en caso de reuniones se muestran los participantes. Ahora gracias a BindableLayout es algo sencillo.

NOTA: En este ejemplo nos hemos centrado en la vista con el timeline, por ese motivo el ejemplo no cuenta con el menu lateral deslizante u otras opciones.

Imágenes circulares

Tenemos muchas opciones para crear imágenes circulares. Entre las opciones disponibles destacan FFImageloading e ImageCirclePlugin. Sin embargo, no son las únicas opciones. A continuación, vamos a crear un pequeño control para tener imágenes circulares usando SkiaSharp.

Tras añadir SkiaSharp.Views.Forms a cada proyecto de la solución, creamos un nuevo control derivado de SKCanvasView:

public class CircularImage : SKCanvasView
{

}

Vamos a necesitar una propiedad para definir la imagen a utilizar:

public static readonly BindableProperty EmbeddedImageNameProperty =
     BindableProperty.Create(nameof(EmbeddedImageName), typeof(string), typeof(CircularImage), "", propertyChanged: OnPropertyChanged);

public string EmbeddedImageName
{
     get { return (string)GetValue(EmbeddedImageNameProperty); }
     set { SetValue(EmbeddedImageNameProperty, value); }
}

Lo que vamos a realizar, es aplicarle un Path a la imagen, recortando en la forma deseada, circular:

SKPath CircularPath = SKPath.ParseSvgPathData("M -1,0 A 1,1 0 1 1 1,0 M -1,0 A 1,1 0 1 0 1,0");

La clave es utilizar el método ClipPath junto con DrawBitmap:

canvas.Clear();

CircularPath.GetBounds(out SKRect bounds);
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.Scale(0.98f * info.Height / bounds.Height);
canvas.Translate(-bounds.MidX, -bounds.MidY);
canvas.ClipPath(CircularPath);
canvas.ResetMatrix();

if (_resourceBitmap != null)
{
     canvas.DrawBitmap(_resourceBitmap, info.Rect);
}

Esto fue sencillo, ¿verdad?. Continuamos.

La barra de navegación

Pasamos a ver la barra de navegación. Utilizamos una NavigationPage donde con la propiedad BarBackgroundColor definimos de forma sencilla el color de fondo. ¿Y el contenido?.

Usamos la propiedad TitleView para definir el contenido personalizado:

<NavigationPage.TitleView>
     <Grid>
          <Label 
               Text="Timeline" 
               Style="{StaticResource BarTitleStyle}"/>
          <Grid
               HorizontalOptions="End"
               Margin="6, 0">
               <controls:CircularImage 
                    EmbeddedImageName="TimelinePulse.Resources.face1.jpg"/>
          </Grid>
     </Grid>
</NavigationPage.TitleView>

Listado

Llegamos al listado. No tiene nada especialmente complejo, pero vamos a ir desglosando cada bloque.

Cada elemento del listado es definido en el ItemTemplate del listado:

<ListView.ItemTemplate>
     <DataTemplate>
          <ViewCell>
               <templates:TaskItemTemplate />
          </ViewCell>
     </DataTemplate>
</ListView.ItemTemplate>

En la definición de cada elemento del listado tenemos una peculiaridad. En caso de reunión, mostramos las personas que asisten. Para ello, hacemos uso de BindableLayout introducido en Xamarin.Forms 3.5:

<StackLayout
     Orientation="Horizontal"
     BindableLayout.ItemsSource="{Binding People}">
     <BindableLayout.ItemTemplate>
          <DataTemplate>
               <Grid>
                    <controls:CircularImage
                         EmbeddedImageName="{Binding Photo}"
                         Style="{StaticResource PhotoStyle}"/>
               </Grid>
          </DataTemplate>
     </BindableLayout.ItemTemplate>
</StackLayout>

Botón con animación

Y llegamos a quizás el “eje” del ejemplo. Sin duda, la parte más detacada, el botón con la animación.

¿Cómo lo conseguimos?

Tenemos diferentes opciones, pero al igual que antes con las imágenes circulares, vamos a usar SkiaSharp.

Comenzamos creando un nuevo control, una nueva clase que herede de SKCanvasView:

public class PulseButton : SKCanvasView
{

}

Definimos algunas propiedades como:

  • EmbeddedImageName: Para poder establecer la imagen del botón.
  • PulseColor: Para definir el color de fondo del botón (circulo).
  • PulseSpeed: Ya que la clave es la animación, mejor tener control sobre la misma.

NOTA: Podríamos definir otras propiedades interesantes como IsAnimating para parar o lanzar la animación, Command para ejecutar una acción, etc. Ten en cuenta que es un ejemplo destinado a cubrir la UI, no una App real.

Para definir el botón, dibujaremos un círculo con el método DrawCircle y en caso de establecer una imagen vía propiedad (creada en el control de forma similar a como hicimos en el control CircularImage), la dibujaremos usando el método DrawBitmap.

canvas.Clear();

SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);

paint.Color = new SKColor(R, G, B);
canvas.DrawCircle(center.X, center.Y, 85, paint);

if (_resourceBitmap != null)
     canvas.DrawBitmap(_resourceBitmap, center.X - _resourceBitmap.Width / 2, center.Y - _resourceBitmap.Height / 2);

Hasta aquí todo muy sencillo. Unas propiedades básicas y un dibujado de un circula e imagen. Con esto tenemos el botón básico, pero…¿y la animación Pulse?.

Vamos a utilizar un StopWatcher para obtener un valor que irá cambiando en base al número de milisegundos que han pasado:

_time = (float)(_stopwatch.Elapsed.TotalMilliseconds % speed / speed);

NOTA: Cada X tiempo debemos refrescar la UI para que el efecto de la animación sea correcto. Esto lo conseguimos con el método InvalidateSurface.

Lo que queda es simple, debemos dibujar otro circulo de igual forma que el anterior pero con un par de detalles:

  • Será mayor a mayor con el paso del tiempo.
  • La opacidad será menor con el paso del tiempo.
SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
float radius = info.Width / 2 * _time;

paint.Color = new SKColor(R, G, B, (byte)(255 * (1 - _time)));
paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle(center.X, center.Y, radius, paint);

Fíjate en el código anterior. Usamos la variable _time (recuerda el StopWatch), para ajustar la opacidad y el radio.

El resultado final:

El resultado

Llegamos hasta aquí. Estamos ante un UI Challenge no muy complejo pero con detalles interesantes.  Espero que te haya resultado interesante. El próximo será mucho más complejo…

Recuerda, cualquier comentario es bienvenida en el artículo!.

Más información

Novedades en Xamarin.Forms 3.6

Introducción

Versión tras versión recibimos novedades en Xamarin.Forms. En la última versión estable (3.6) hemos recibimos múltiples novedades de peso (donde brilla con luz propia Visual). ¿Y si las repasamos todas?.

Novedades

A continuación, vamos a realizar un repaso de todas las principales novedades introducidas en esta versión.

Visual

A la hora de desarrollar aplicaciones multiplataforma, buscamos llegar a diferentes plataformas cada una de ellas con sus propias guías de estilo, controles e idiosincrasia. Sin embargo, en ocasiones como desarrolladores móviles buscamos conseguir el mismo aspecto. Con Xamarin.Forms, a pesar de contar con una capa de abstracción en la capa de UI permitiendo definir la interfaz una única vez, creamos aplicaciones nativas. Es decir, utilizando Xamarin.Forms utilizamos los controles nativos de cada plataforma. Para conseguir un mismo aspecto necesitamos utilizar Custom Renderers o efectos en ocasiones.

Con Visual nos llega una nueva forma de indicar una forma de renderizar los controles nativos.

NOTA: Utilizando Visual seguimos utilizando controles 100% nativos.

Para establecer el uso de Visual, utilizaremos la propiedad Visual:

<ContentPage 
     Visual="Material">

</ContentPage>

La propiedad Visual esta disponible en la clase VisualElement. Cualquier elemento hijo de otro heredará el valor de la propiedad y modificará su forma de renderizar. Por este motivo, si establecemos Visual a nivel de ContentPage aseguramos que cualquier elemento de la página utilizará el nuevo sistema de renderizado.

NOTA: El valor de la propiedad Visual puede cambiar en tiempo de ejecución.

Nos llega esta funcionalidad en modo Preview con la apariencia basada en Material Design.

Además, se incluye la posibilidad de crear Custom Renderer para Visual.

ItemsSource e ItemTemplate en mapas

En esta versión se añaden dos nuevas propiedades el control de tipo mapa:

  • ItemsSource: Especifica la colección de tipo IEnumerable de elementos a mostrar.
  • ItemTemplate: DataTemplate a utilizar en cada elemento de la colección.

Un ejemplo:

<map:Map
     ItemsSource="{Binding Places}">
          <map:Map.ItemTemplate>
          <DataTemplate>
               <map:Pin 
                    Position="{Binding Position}"
                    Address="{Binding Address}"
                    Label="{Binding Description}" />
          </DataTemplate>
     </map:Map.ItemTemplate>
</map:Map>

Y el resultado:

ItemsSource e ItemTemplate en mapas!

FontImageSource

Se añade un nuevo tipo ed imagen, la clase FontImageSource derivada de ImageSource. Permite mostrar iconos que provienen de fuentes.

Cuenta con las siguientes propiedades:

  • Glyph: Valor de tipo cadena que especifica el caracter a utilizar de la fuente.
  • Color: Color a usar en el Glyph.
  • FontFamily: La fuente a utilizar.
  • Size: Valor de tipo double que especifica el tamaño a utilizar al renderizar el icono. El valor por defecto es 30.

Veamos un ejemplo:

<Image
     BackgroundColor="Gray">
     <Image.Source>
          <FontImageSource 
               Glyph="{Binding Glyph}"
               FontFamily="{OnPlatform iOS=Ionicons, Android=ionicons.ttf#}"
               Size="44"/>
     </Image.Source>
</Image>

Y el resultado:

FontImageSource

 

ReadOnly Entry

Una opción muy solicitada era la posibilidad de tener Entry de solo lectura. Se añade la propiedad IsReadOnly.

<Entry 
     Text="Content..."
     IsReadOnly="true"/>

A diferencia de IsEnabled, esta nueva propiedad no modifica el aspecto visual del control.

Otras novedades

En esta versión se han incluidos otras novedades también interesantes como:

Más información

[Material] Novedades en Xamarin.Forms 4: Shell, CollectionView y más!

El evento

El pasado Jueves 11, celebramos un nuevo evento en SVQXDG (grupo de desarrolladores de Xamarin en Sevilla) donde repasamos todas las últimas novedades de Xamarin.Forms.

Repasando novedades de Xamarin.Forms

El material

A continación, vamos a repasar todo el material (presentación y ejemplos) que vimos en la presentación.

Novedades de Xamarin.Forms 3.5

Comenzamos el evento viendo las últimas novedades en las versiones estables, empezando por la versión 3.5. Vimos:

  • BindableLayout
  • ScrollBarVisibility
  • SwitchCell OnColor
  • Novedades en accesibilidad
  • Otras novedades

En cuanto al ejemplo, esta disponible en GitHub:

Ver GitHub

Novedades en Xamarin.Forms 3.6

Continuamos viendo las novedades de la última versión estable de Xamarin.Forms a la hora de escribir este artículo, la versión 3.6. Novedades:

  • Visual.
  • ItemsSource e ItemTemplate en mapas.
  • FontImageSource.
  • Otras novedades.

En cuanto al ejemplo, esta disponible en GitHub:

Ver GitHub

Visual

A la hora de desarrollar aplicaciones multiplataforma, buscamos llegar a diferentes plataformas cada una de ellas con sus propias guías de estilo, controles e idiosincrasia. Sin embargo, en ocasiones como desarrolladores móviles buscamos conseguir el mismo aspecto. Con Xamarin.Forms, a pesar de contar con una capa de abstracción en la capa de UI permitiendo definir la interfaz una única vez, creamos aplicaciones nativas. Es decir, utilizando Xamarin.Forms utilizamos los controles nativos de cada plataforma. Para conseguir un mismo aspecto necesitamos utilizar Custom Renderers o efectos en ocasiones.

Con Visual nos llega una nueva forma de indicar una forma de renderizar los controles nativos.

Hicimos un repaso como usar y que ofrece Visual. Espero que en próximos eventos veamos como crear renderers personalizados con Visual y otras opciones.

CollectionView

Continuamos con más novedades!. Llegaba el momento de hablar de CollectionView creando listados, listados horizontales, diferentes layouts con columnas, etc.

En cuanto al ejemplo, esta disponible en GitHub:

Ver GitHub

Shell

Llegamos al plato fuerte del evento, y la novedad principal con Xamarin.Forms 4, Shell. Vimos:

  • Introducción a Shell.
  • ShellItem, ShellSection y ShellContent.
  • Nuevas opciones al navegar.
  • Crear Flyouts.
  • Barras de búsqueda.
  • Otras opciones (ocultar tabs o nav, etc.).

El ejemplo:

Ver GitHub

Otras novedades

Para terminar el evento, vimos novedades de terceros. En concreto, repasamos Aurora Controls y Grial UI Kit 3.

Como de costumbre, un placer compartir momentos en comunidad. Muchas gracias a todos los asistentes, nos vemos en la próxima!. Pero…un momento. Me gustaría terminar recordando que la comunidad es de todos. Si te apetece hablar de tecnologías .NET o Xamarin, tan solo avísame!.

Más información

[VideoBlog] Un vistazo a Aurora Controls

Introducción

La evolución de Xamarin.Forms es constante. Cada poco tiempo recibimos novedades en forma de nuevos controles (CollectionView o ImageButton por ejemplo), nuevas propiedades, Platform Specifics, etc. con el objetivo de simplificar la creación y personalización de interfaces de usuario.

De igual forma, la comunidad y empresas terceras cada vez lanzan plugins, controles y herramientas de más calidad. Recientemente, recibimos un nuevo paquete de controles, efectos, extensiones, gráficas, etc. llamado Aurora Controls.

Aurora Controls

Aurora Controls ofrece una variedad de controles, extensiones, efectos, gráficas, controles de carga y varios helpers con el objetivo de cubrir necesidades habituales a la hora de desarrollar interfaces de usuario en Xamarin.Forms (degradados, sombras, capturar datos de diferente tipo, etc.).

Una de las características más destacable de una gran parte de los controles es el uso de SkiaSharp para realizar el dibujado de los mismos. Tenéis el código fuente del ejemplo utilizado disponible en GitHub:

Ver GitHubRecordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[SVQXDG] Nuevo evento: Un repaso a las últimas novedades en Xamarin.Forms

El evento

Si repasamos la lista de grandes novedades de Xamarin.Forms de los últimos meses encontramos una cantidad elevada de cambios. Hablamos de Shell, Visual, BindableLayout, Bindable Maps, etc que sumado a muchos pequeños detalles (FontIconSource, Read only Entry, etc.) nos brinda nuevas posibilidades.

Xamarin.Forms

¿Y si las recopilamos y vemos todas en un evento?.

El lugar

El evento se celebrará en la ETS de Ingeniería Informática. Dirección detallada:

E.T.S. Ingeniería Informática – Universidad de Sevilla, Aula por confirmar
Av. Reina Mercedes s/n
Sevilla Se 41012

La fecha

Será el próximo Jueves, 11 de Abril de 20:00h a 21:00h (GMT+1).

¿Te apuntas?

Más información

[Xamarin.Forms Challenge] My Tasks

Introducción

Según evoluciona de Xamarin.Forms, llegan más y más opciones que simplifican la creación de diferentes elementos de la interfaz de usuario.

En el estado actual de Xamarin.Forms se pueden conseguir aplicaciones nativas de gran escala, con interfaces cuidadas y con alta integración con la plataforma. Hay que tener en cuenta el conjunto de Custom Renderers (código específico en cada plataforma) necesario para lograrlo.

NOTA: La elección entre Xamarin Classic o Xamarin.Forms es importante. Es necesario evaluar la aplicación a desarrollar, el conjunto de características específicas de cada plataforma (que pueden requerir un Custom Renderer), etc. 

En este artículo, vamos a tomar como referencia un diseño de Dribbble (por Anton Aheichanka), que intentaremos replicar con Xamarin.Forms paso a paso.

El diseño

My Tasks

Vamos a intentar replicar la UI del diseño paso a paso en Xamarin.Forms.

Los retos del ejemplo

Vamos a comenzar haciendo un análisis de la interfaz de usuario desglosando los elementos que la componen:

  • Cabecera: La cabecera cuenta con información del perfil de usuario. La imagen del perfil es circular algo que es sencillo de conseguir de diversas formas (FFImageLoading con transformaciones, ImageCirclePlugin o incluso con SkiaSharp). La otra característica destacada de la cabecera es la imagen de fondo ya que tiene un corte horizontal sencillo pero que hace destacar aun más la cabecera. Entre las opciones que tenemos, una de las más sencillas es utilizar SkiaSharp.
  • El listado: El listado cuenta con una cabecera sencilla pero elegante, fácil de conseguir con la propiedad Header del ListView; elementos que se pueden conseguir definiendo una celda personalizada. En la celda, en caso de reuniones se muestran los participantes. Ahora gracias a BindableLayout es algo sencillo.
  • El menu de filtro: La característica principal de la vista. Una de mis APIs favoritas en Xamarin.Forms es la de animaciones. Sencilla pero ofrece una libertas muy alta. En este caso, podemos replicarlo usando una ContentView con imágenes en el interior y el efecto de apertura y cierre del menu lo conseguimos a base de animaciones (de escala, opacidad, etc.).

Imagen con corte horizontal

¿Cómo conseguimos la imagen con corte horizontal?

SkiaSharp suele ser una opción ideal para opciones de dibujado. En este caso, nos vendría genial directamente dibujar la imagen pero recortando una pequeña parte (triangulo) de la parte inferior. SkiaSharp permite cargar imágenes además de tener una enorme variedad de posibilidades al tratar la misma. aplicar diferentes transformaciones, efectos, etc. Podemos hacer un recorte con trazado (un path) de una imagen.

Tras añadir los paquetes de SkiaSharp.Views.Forms a la solución, creamos un nuevo control que herede de SKCanvasView.

Comenzamos definiendo el trazado que usaremos para recortar la imagen:

SKPath Path = SKPath.ParseSvgPathData("m 0 0 l 400 0 l 0 300 l -400 -50");

Usamos el método ClipPath para hacer el recorte:

canvas.ClipPath(Path);

Para dibujar la imagen usamos SKBitmap que pintaremos con el método DrawBitmap:

canvas.DrawBitmap(_bitmap, info.Rect);

Sencillo, ¿verdad?

Listado

Llegamos al listado. No tiene nada especialmente complejo, pero vamos a ir desglosando cada bloque. Comenzamos por la cabecera. Nos apoyamos en la propiedad Header del ListView para definir la misma:

<ListView.Header>
     <templates:TaskHeaderTemplate />
</ListView.Header>

Donde TaskHeaderTemplate es un ContentView con la definición de la cabecera.

Cada elemento del listado es definido en el ItemTemplate del listado:

<ListView.ItemTemplate>
     <DataTemplate>
          <ViewCell>
               <templates:TaskItemTemplate />
          </ViewCell>
     </DataTemplate>
</ListView.ItemTemplate>

En la definición de cada elemento del listado tenemos una peculiaridad. En caso de reunión, mostramos las personas que asisten. Para ello, hacemos uso de BindableLayout introducido en Xamarin.Forms 3.5:

<StackLayout
     Orientation="Horizontal"
     BindableLayout.ItemsSource="{Binding People}">
     <BindableLayout.ItemTemplate>
          <DataTemplate>
               <Grid>
                    <imageCircle:CircleImage 
                    Source="{Binding Photo}"
                    Aspect="AspectFit"
                    Style="{StaticResource PhotoStyle}"/>
               </Grid>
          </DataTemplate>
     </BindableLayout.ItemTemplate>
</StackLayout>

Pero nos sigue quedando una peculiaridad del ejemplo en el listado…

Animando cada elemento del listado

Cada vez que se añaden o quitan elementos al listado, se introducen con una breve pero efectiva animación de Fade In y translación hacia arriba.

¿Cómo lo conseguimos?

Vamos a crear una ViewCell personalizada. Para ellos creamos una clase que here de ViewCell y al añadir un elemento hijo:

protected override void OnChildAdded(Element child)
{
     base.OnChildAdded(child);

     uint duration = 750;

     var animation = new Animation();

     animation.WithConcurrent((f) => TaskItemTemplate.Opacity = f, 0, 1, Easing.CubicOut);

     animation.WithConcurrent(
     (f) => TaskItemTemplate.TranslationY = f,
     TaskItemTemplate.TranslationY + 50, 0,
     Easing.CubicOut, 0, 1);

     TaskItemTemplate.Animate("FadeIn", animation, 16, Convert.ToUInt32(duration));
}

La definición del ItemTemplate del ListView debe cambiar a algo como:

<ListView.ItemTemplate>
     <DataTemplate>
          <cells:TaskItemViewCell />
     </DataTemplate>
</ListView.ItemTemplate>

Voila!

Filtro circular

Y llegamos a la parte más atractiva del ejemplo.

¿Cómo conseguirla de forma sencilla?

Vamos a crear un ContentView que contendrá primero la imagen del menu cerrado. Por otro lado, la imagen para cerrar el menu y el fondo expandido además de claro, una imagen por cada elemento del menu.

Haremos uso de GestureRecognizers para capturar las pulsaciones en el menu o cada elemento del menú. Al pulsar haremos dos acciones.

Por un lado, lanzaremos un comando definido en el control:

public static readonly BindableProperty SelectedCommandProperty =
     BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(FilterMenu), null);

public ICommand SelectedCommand
{
     get { return (ICommand)GetValue(SelectedCommandProperty); }
     set { SetValue(SelectedCommandProperty, value); }
}

Y por otro lado, haremos uso de animaciones!. Veamos por ejemplo, que hacemos para abrir el menu:

await InnerButtonMenu.RotateTo(360, _animationDelay);
await InnerButtonMenu.FadeTo(0, _animationDelay);
await InnerButtonClose.RotateTo(360, _animationDelay);
await InnerButtonClose.FadeTo(1, _animationDelay);
await OuterCircle.ScaleTo(3.5, 100, Easing.Linear);
await N.FadeTo(1, speed);
await NW.FadeTo(1, speed);
await SW.FadeTo(1, speed);
await S.FadeTo(1, speed);

Ocultamos con una rotación el botón (imagen) para abrir el menu. a continuación mostramos el botón de cerrar y escalamos (importante!) el area del menu. Por último, cambiamos la opacidad de cada elemento del menu.

Sencillo, pero realmente espectacular y efectivo.

Esta idea de menu esta basada en un gran ejemplo de Alan Beech que Ricardo Vasquez ha elaborado más para tener un control.

El resultado conseguido:

El resultado

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

Hasta aquí el desglose del ejemplo. Lo terminamos sin Custom Renderers o efectos, haciendo uso de Xamarin.Forms y sus APIs para tareas complejas como el menú de filtro o el listado y apoyándonos en SkiaSharp para algunas tareas.

Cualquier duda o comentario es bienvenido en los comentarios!

Más información

[Xamarin.Forms] Utilizando Bindable Layouts

Introducción

En Xamarin.Forms tenemos un tipo especial de View llamada Layout. Un Layout es un contenedor para otros elementos permitiendo ayudar a posicionar y gestionar el tamaño de los elementos que contiene. En Xamarin.Forms contamos con una gran variedad de Layouts:

Layouts en Xamarin.Forms

Los más utilizados son el StackLayout y el Grid, y suele ser habitual hacer una composición de varios así como utilizarlos para crear controles, etc. Por ejemplo, en ocasiones se utiliza la combinación de ScrollView y StackLayout junto con ContentViews para crear un pequeño listado de elementos horizontal.

Bindable Layout

Con la llegada de Xamarin.Forms 3.5 pre2 nos llega Bindable Layout. En toda clase derivada de Layout<T>, contamos ahora con las siguientes propiedades:

  • ItemsSource: De tipo IEnumerable, soporta el enlace de una colección de datos.
  • ItemTemplate: De tipo DataTemplate, permitirá definir la apariencia visual de cada elemento.
  • ItemTemplateSelector: De tipo ItemTemplateSelector, permite poder elegir entre diferentes templates para cada elemento en base a ciertas condiciones.

Las propiedades resultan familiares conociendo otros controles en Xamarin.Forms como el ListView.

¿Recuerdas el ejemplo que hemos mencionado de listado horizontal?. Utilizando Bindable Layout todo se simplifica.

Uso básico

De la interfaz que estamos replicando, el caso más sencillo es el listado de amigos.

<StackLayout
     Orientation="Horizontal"
     BindableLayout.ItemsSource="{Binding Profile.Friends}">
     <BindableLayout.ItemTemplate>
          <DataTemplate>
          <Grid>
               <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
               </Grid.RowDefinitions>
                    <imageCircle:CircleImage 
                         Grid.Row="0"
                         Source="{Binding Picture}"
                         Aspect="AspectFit"
                         Style="{StaticResource FriendImageStyle}"/>
                    <Label 
                    Grid.Row="1"
                         Text="{Binding Name}"
                         Style="{StaticResource FriendNameStyle}"/>
               </Grid>
          </DataTemplate>
     </BindableLayout.ItemTemplate>
</StackLayout>

El resultado:

Bindable StackLayout

Sencillo, ¿verdad?.

Utilizando Bindable Layout con DataTemplateSelector

Pasamos a una necesidad más compleja, la galería. En la galería vamos a necesitar mostrar elementos con diferente tamaño.

¿Qué podemos hacer?

Vamos a analizar que necesitamos:

  • Los elementos hacen wrapping.
  • Los elementos se organizan de izquierda a derecha.
  • Hay imágenes de diferente tamaño.

FlexLayout nos permitirá ambas opciones. Definimos el control:

<FlexLayout
     Style="{StaticResource GalleryStyle}" />

Utilizamos un estilo para habilitar el Wrapping, etc. A continuación, vamos a hacer uso de Bindable Layout para evitar definir N imágenes dentro del Layout.

<FlexLayout
     BindableLayout.ItemsSource="{Binding Profile.Gallery}"
     BindableLayout.ItemTemplateSelector="{StaticResource GalleryItemTemplateSelector}"
     Style="{StaticResource GalleryStyle}" />

Al contar con la necesidad de tener imágenes de diferente tamaño en la galería, vamos a utilizar la propiedad ItemTemplateSelector para utilizar diferentes plantillas:

<styles:GalleryItemTemplateSelector x:Key="GalleryItemTemplateSelector">
     <styles:GalleryItemTemplateSelector.MediumGalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}"
                    Aspect="AspectFill"
                    StyleClass="photo, medium"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.MediumGalleryItemTemplate>
     <styles:GalleryItemTemplateSelector.BigGalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}"
                    Aspect="AspectFill"
                    StyleClass="photo, big"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.BigGalleryItemTemplate>
     <styles:GalleryItemTemplateSelector.GalleryItemTemplate>
          <DataTemplate>
               <Image 
                    Source="{Binding Picture}" 
                    Aspect="AspectFill"
                    StyleClass="photo"/>
          </DataTemplate>
     </styles:GalleryItemTemplateSelector.GalleryItemTemplate>
</styles:GalleryItemTemplateSelector>

El resultado:

Bindable FlexLayout

Veamos el resultado completo:

El resultado

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

¿Qué te parece Layout Bindable?. Recuerda, puedes dejar cualquier duda o comentario en la entrada!

Más información

[Preview] Primer vistazo a Xamarin.Forms CollectionView

Introducción

Como parte de las novedades que llegan con Xamarin.Forms 4.0 se encuentra el nuevo control CollectionView.

Xamarin.Forms CollectionView

CollectionView llega con el objetivo de mejorar lo ofrecido por el control ListView hasta ahora:

  • Facilitar más opciones como listados horizontales o Grids.
  • Cubrir opciones muy solicitadas (vista para cuando no hay contenido, etc.).
  • Mejorar rendimiento.
  • Etc.

Primeros pasos

Para habilitar CollectionView es necesario utilizar un flag antes de inicializar Xamarin.Forms (recuerda, estamos ante una preview).

global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental");

Layout básico

Podemos conseguir el mismo Layout de un ListView de forma sencilla. Como ventaja, con CollectionView no es necesario utilizar celdas, es decir, no usaremos ViewCell. Esto tiene un impacto en el rendimiento, sobretodo en Android, pero manteniendo la estructura y Layout del ListView.

<CollectionView 
     ItemsSource="{Binding Monkeys}">
     <CollectionView.ItemTemplate>
          <DataTemplate>
               <Grid 
                    HeightRequest="48">
                    <Label 
                         Text="{Binding Name}"
                         FontSize="18"
                         VerticalOptions="Center"/>
               </Grid>
          </DataTemplate>
     </CollectionView.ItemTemplate>
</CollectionView>

El resultado:

Layout básico

Fíjate que usamos un DataTemplate sin necesidad de ViewCell!.

Listas horizontales

Por defecto, el CollectionView utilizar un layout lineal. Esto quiere decir que se pueden hacer listas verticales pero…también horizontales!.

El control CollectionView cuenta con la propiedad ItemsLayout. Podemos establecer esta propiedad a una nueva instancia de GridItemsLayout que cuenta con una propiedad Orientation para definir la orientación.

<CollectionView.ItemsLayout>
     <GridItemsLayout
          Orientation="Horizontal" />
</CollectionView.ItemsLayout>

El resultado:

Listas horizontales

Carousel

Algo similar pero más complejo, un Carousel. Contar con un elemento scrollable pero navegando de elemento en elemento. Contamos con un control llamado CarouselView, un control nuevo basado en CollectionView, que podemos utilizar para conseguir el formato clásico de “carta” y paso de elemento a elemento:

<CarouselView 
     ItemsSource="{Binding Monkeys}">
     <CarouselView.ItemsLayout>
          <GridItemsLayout
               Orientation="Horizontal" 
               SnapPointsAlignment="Center" 
               SnapPointsType="Mandatory"/>
     </CarouselView.ItemsLayout>
</CarouselView>

El resultado:

Carousel

Una de las características más interesantes que tenemos disponible es la posibilidad de controlar el comportamiento y alineación de cada elemento al hacer el desplazamiento. Lo conseguimos con las propiedades SnapPointsType y SnapPointsAlignment.

Grid

Por defecto, CollectionView utiliza un layout lineal. Es decir, por defecto podemos hacer listas verticales u horizontales. Como ya vimos previamente, con la propiedad ItemsLayout podemos modificar el comportamiento del Layout.

<CollectionView.ItemsLayout>
     <GridItemsLayout
          Orientation="Vertical"
          Span="2"/>
</CollectionView.ItemsLayout>

Utilizando GridItemsLayout podemos controlar la orientación con la propiedad Orientation y con la propiedad Span el número de elementos por fila o columna.

Grid Layout

Vista cuando no hay contenido

¿Qué ocurre cuando ItemsSource no tiene elementos?. Probablemente, hasta ahora con el ListView has jugado con paneles y visibilidad para dar feedback al usuario ante esta situación. CollectionView viene a mejorar esto. Podemos utilizar la propiedad EmptyView para mostrar cualquier contenido en caso de tener elementos.

<CollectionView 
     ItemsSource="{Binding Monkeys}">
     <CollectionView.EmptyView>
          <Grid>
          <Label 
               Text="No items"
               HorizontalOptions="Center"
               VerticalOptions="Center"/>
          </Grid>
     </CollectionView.EmptyView>
</CollectionView>

El resultado:

Sin contenido

Sencillo, ¿verdad?.

Tienes el ejemplo disponible en GitHub:

Ver GitHub¿Qué te parece?, ¿qué te resulta más interesante?. Recuerda, puedes dejar comentarios en la entrada!.

Más información

[Preview] Primer vistazo a Xamarin.Forms Visual

Introducción

A la hora de desarrollar aplicaciones multiplataforma, buscamos llegar a diferentes plataformas cada una de ellas con sus propias guías de estilo, controles e idiosincrasia. Sin embargo, en ocasiones como desarrolladores móviles buscamos conseguir el mismo aspecto. Con Xamarin.Forms, a pesar de contar con una capa de abstracción en la capa de UI permitiendo definir la interfaz una única vez, creamos aplicaciones nativas. Es decir, utilizando Xamarin.Forms utilizamos los controles nativos de cada plataforma. Para conseguir un mismo aspecto necesitamos utilizar Custom Renderers o efectos en ocasiones.

Xamarin.Forms Visual

Con Xamarin.Forms 4.0-pre1 nos llega una nueva forma de indicar una forma de renderizar los controles nativos con Visual.

Cuando utilizamos Visual, se utiliza el renderizado personalizado en cada plataforma en lugar del renderizado predeterminado.

NOTA: Utilizando Visual seguimos utilizando controles 100% nativos.

Para establecer el uso de Visual, utilizaremos la propiedad Visual:

<ContentPage 
     Visual="Material">

</ContentPage>

La propiedad Visual esta disponible en la clase VisualElement. Cualquier elemento hijo de otro heredará el valor de la propiedad y modificará su forma de renderizar. Por este motivo, si establecemos Visual a nivel de ContentPage aseguramos que cualquier elemento de la página utilizará el nuevo sistema de renderizado.

NOTA: El valor de la propiedad Visual puede cambiar en tiempo de ejecución.

Nos llega esta funcionalidad en modo Preview con la apariencia basada en Material Design. Actualmente, en la versión 4.0-pre1 contamos con Material Design en Android e iOS para los siguientes controles:

  • Button
  • Entry
  • Frame
  • ProgressBar

Para utilizar Visual, por ahora (recuerda, estamos ante una Preview) es necesario añadir la siguiente línea:

Forms.SetFlags("Visual_Experimental");

Antes de llamar a Forms.Init. Además, en iOS, es necesario añadir el paquete NuGet Xamarin.iOS.MaterialComponents.

NOTA: Visual requiere API 29 para funcionar en Android.

Veamos un ejemplo:

<Grid
     BackgroundColor="DarkGray">
     <Frame 
          Style="{StaticResource SignInFrameStyle}">
          <StackLayout 
               Style="{StaticResource SignInPanelStyle}">
               <Label 
                    Text="Sign in"
                    Style="{StaticResource SignInTitleStyle}" />
               <Entry 
                    Placeholder="Username" />
               <Entry 
                    IsPassword="True"
                    Placeholder="Password" />
               <Button 
                    Text="Sign in" 
                    Style="{StaticResource SignInButtontyle}"/>
          </StackLayout>
     </Frame>
</Grid>

El resultado:

Tienes el ejemplo disponible en GitHub:

Ver GitHub

Más información