[Xamarin.Forms Challenge] My Trip Countdown

Introducción

La evolución de Xamarin.Forms es meritoria. En los últimos tiempos se han recibido novedades interesantes como efectos, vistas nativas, Forms Embedding, FlexLayout, etc. Sin embargo, en muchos casos se sigue asociado a desarrollos muy simples o formularios básicos.

Realmente, 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 Dribble, que intentaremos replicar con Xamarin.Forms paso a paso.

Countdown Timer

Lo primero que debemos realizar, es un breve análisis de la pantalla:

  • Simplificando, estamos ante un contador hacia atrás, en concreto, hacia una fecha. En Xamarin.Forms contamos con la clase Device, una clase importante que nos facilita información importante como si estamos en una plataforma u otra, o si ejecutamos la App en teléfono o tableta. Además, Device cuenta con un Timer que podemos utilizar para la cuenta atrás, entre otras opciones.
  • De lo que más llama la atención de la simple interfaz de usuario, son los degradados. La línea de progreso circular es candidata a realizarse con SkiaSharp. A pesar de poder abordarla con un Custom Renderer, SkiaSharp nos facilitará dibujarla una única vez para todas las plataformas. El botón, a pesar de poder abordarlo también con SkiaSharp, vamos a realizarlo en nativo para poder contar con los diferentes estados, etc. En este caso, vamos a necesitar Custom Renderer o plugin de comunidad.
  • La carátula circular no es compleja. Entre en conjunto de posibilidades que tenemos, FFImageLoading gana enteros por su facilidad de uso, opciones de cache además de transformaciones.
  • La rotación de la carátula es una sencilla animación de rotación. Xamarin.Forms cuenta con una API de animaciones bastante completa.

¿Manos a la obra?

Imagen circular

Comenzamos por una de las partes principales de la vista, la imagen circular. Vamos a utilizar FFImageLoading junto a las opciones de transformación disponibles:

xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:fftransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"

Vamos a utilizar CircleTransformation para conseguir la imagen circular:

<ffimageloading:CachedImage 
     Aspect="AspectFit"
     Source="{Binding MyTrip.Picture}">
     <ffimageloading:CachedImage.Transformations>
          <fftransformations:CircleTransformation />
     </ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>

Sencillo, ¿verdad?.

Progreso circular con degradado

Sin duda alguna, la clave de la vista es la barra de progreso circular… y con degradado!. Por un lado, podemos irnos a por Custom Renderer y aprovechar código nativo en cada plataforma. Si queremos conseguir todo sólo con código compartido podríamos conseguir una barra circular de forma sencilla con imágenes y rotaciones, por ejemplo. Sin embargo, necesitamos diferentes recursos gráficos para adaptarnos a diferentes resoluciones. Por otro lado, el degradado añade complejidad.

¿La solución sencilla?

SkiaSharp. Engine gráfico 2D disponible para iOS, Android, UWP, macOS, Windows, etc.

Nos permite trabajar de forma sencilla desde co n figuras básicas a efectos y shaders más complejos.

En este caso, vamos a crear un control personalizado que herede de la clase SKCanvasView.

SKCanvasView es una vista Xamarin.Forms donde podremos dibujar utilizando SkiaSharp.

public class CircleCountdown : SKCanvasView
{

}

Vamos a crear varias BindableProperties para gestionar el comportamiento y apariencia de la barra de progreso:

public static readonly BindableProperty StrokeWidthProperty =
     BindableProperty.Create(nameof(StrokeWidth), typeof(float), typeof(CircleCountdown), 10f, propertyChanged: OnPropertyChanged);

public static readonly BindableProperty ProgressProperty =
     BindableProperty.Create(nameof(Progress), typeof(float), typeof(CircleCountdown), 0f, propertyChanged: OnPropertyChanged);

public static readonly BindableProperty ProgressStartColorProperty =
     BindableProperty.Create(nameof(ProgressStartColor), typeof(Color), typeof(CircleCountdown), Color.Blue, propertyChanged: OnPropertyChanged);

public static readonly BindableProperty ProgressEndColorProperty =
     BindableProperty.Create(nameof(ProgressEndColor), typeof(Color), typeof(CircleCountdown), Color.Red, propertyChanged: OnPropertyChanged);

public float StrokeWidth
{
     get { return (float)GetValue(StrokeWidthProperty); }
     set { SetValue(StrokeWidthProperty, value); }
}

public float Progress
{
     get { return (float)GetValue(ProgressProperty); }
     set { SetValue(ProgressProperty, value); }
}

public Color ProgressStartColor
{
     get { return (Color)GetValue(ProgressStartColorProperty); }
     set { SetValue(ProgressStartColorProperty, value); }
}

public Color ProgressEndColor
{
     get { return (Color)GetValue(ProgressEndColorProperty); }
     set { SetValue(ProgressEndColorProperty, value); }
}

Contamos con:

  • StrokeWidth: Permite controlar el grosor de la barra de progreso.
  • Progress: Determina el progreso. Acepta un valor numérico entre 0  y 1.
  • ProgressStartColor: ¿Recuerdas la necesidad de crear degradado?. Por ese motivo, vamos a contar con esta propiedad para gestionar el color de inicio del degradado.
  • ProgressEndColor: Color final del degradado.

Para dibujar el progreso circular, utilizaremos el método DrawPath para dibujar un Path al que le daremos la forma deseada utilizando el método AddArc.

using (SKPath path = new SKPath())
{
     path.AddArc(rect, StartAngle, angle);

     canvas.DrawPath(path, paint);
}

¿Y el degradado?.

En lugar de utilizar un SKColor (color en SkiaSharp) sólido, vamos a crear un shader. Utilizaremos el método CreateSweepGradient para crear el degradado:

var shader = SKShader.CreateSweepGradient(
     new SKPoint(size / 2, size / 2),
     new[]
     {
          ProgressStartColor.ToSKColor(),
          ProgressEndColor.ToSKColor(),
          ProgressStartColor.ToSKColor()
     },
     new[]
     {
          StartAngle / 360,
          (StartAngle + progressAngle + 1) / 360,
          (StartAngle + progressAngle + 2) / 360
});

Para utilizar el control, definimos el namespace:

xmlns:controls="clr-namespace:MyTripCountdown.Controls"

Y utilizamos el control:

<controls:CircleCountdown 
     VerticalOptions="FillAndExpand"
     HorizontalOptions="FillAndExpand"
     Progress="{Binding Progress}"
     ProgressStartColor="{StaticResource ProgressPinkColor}"
     ProgressEndColor="{StaticResource ProgressBlueColor}"
     StrokeWidth="10"/>

El progreso lo tenemos enlazado a una propiedad en la ViewModel donde se irá realizando el cálculo del progreso.

Botón con degradado

Llegamos al botón con degradado. Podríamos al igual que la barra de progreso circular, crearlo con SkiaSharp facilmente. Sin embargo, un botón cuenta con estados que tienen importancia. Habilitado, deshabilitado, pulsado, etc. Si queremos contar con un botón, debemos crear un Custom Renderer. No es complejo haciendo un Custom Renderer del botón y utilizando RippleDrawable con GradientDrawable en Android, y CAGradientLayer en iOS.

Sin embargo, la comunidad de Xamarin es fantástica. Contamos con un botón con degradado preparado para utilizar con Skor.UI. Tras añadir el paquete NuGet, añadimos el namespace:

xmlns:skor="clr-namespace:Skor.Controls;assembly=Skor.Controls"

Y utilizamos el control:

<skor:GradientButton 
     HeightRequest="60" 
     CornerRadius="96"
     StartColor="{StaticResource ProgressPinkColor}"
     EndColor="{StaticResource ProgressBlueColor}"
     Command="{Binding RestartCommand}"/>

De nuevo, muy sencillo esta parte de la interfaz, ¿verdad?.

Otros

Y no hay que olvidar que estamos haciendo un contador hacia atrás. ¿Formas de conseguirlo?. Tenemos diferentes opciones, directamente en Xamarin.Forms, tenemos el método StartTimer en la clase Device:

Device.StartTimer(TimeSpan.FromSeconds(seconds), () =>
{
     RemainTime = (EndDate - DateTime.Now);

     var ticked = RemainTime.TotalSeconds > 1;

     if (ticked)
     {
          Ticked?.Invoke();
     }
     else
     {
          Completed?.Invoke();
     }

     return ticked; 
});

¿Qué plugins o componentes se han utilizado?

Se ha utilizado:

  • FFImageLoading – Con el objetivo principal de gestionar la imágen circular. Esta librería también nos facilita importante funcionalidad relacionada con el cacheo de imágenes, etc. Aunque recuerda, en este ejemplo todas las imágenes son locales.
  • SKOR.UI – Nos facilita la creación de botones con degradados evitandonos crear un Custom Renderer.

Y llegamos a la parte final del artículo. Es un concepto de artículo que ya hemos realizado previamente con la creación de la interfaz de Netflix, por ejemplo. Es sumamente divertido preparar un diseño con cierto nivel de “reto” e intetar “desgranar” cada paso a realizar. Pero, ¿qué te parece este tipo de artículos?.

Cualquier duda o comentario es bienvenido en los comentarios!

El resultado

My Trip Countdown

Puedes encontrar el ejemplo en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Ejemplos de Backend Linux

Introducción

Xamarin.Forms añade una capa de abstracción en la capa de UI que nos permite definir la misma una única vez para todas las plataformas. Podemos definir esta interfaz con código C# o XAML. El soporte de Linux (GTK) ahora está  disponible en Xamarin.Forms.

Para conocer y revisar las diferentes opciones disponibles, ¿algo mejor que contar con ejemplos?

Ejemplos Xamarin.Forms.Platform.GTK

El repositorio de ejemplos oficial de Xamarin.Forms es una fuente inmejorable de ejemplos cubriendo desde ejemplos básicos a uso de SkiaSharp, SQlite, gestos, efectos, etc. A continuación, puedes encontrar un repositorio donde encontrar la mayoría de ejemplos (y creciendo!) con versión GTK.

Puedes encontrar el repositorio en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Ejemplos de Backend WPF

Introducción

Xamarin.Forms añade una capa de abstracción en la capa de UI que nos permite definir la misma una única vez para todas las plataformas. Podemos definir esta interfaz con código C# o XAML. El soporte de WPF ahora está  disponible en Xamarin.Forms.

Para conocer y revisar las diferentes opciones disponibles, ¿algo mejor que contar con ejemplos?

Ejemplos Xamarin.Forms.Platform.WPF

El repositorio de ejemplos oficial de Xamarin.Forms es una fuente inmejorable de ejemplos cubriendo desde ejemplos básicos a uso de SkiaSharp, SQlite, gestos, efectos, etc. A continuación, puedes encontrar un repositorio donde encontrar la mayoría de ejemplos (y creciendo!) con versión WPF.

Puedes encontrar el repositorio en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Utilizando LiteDB

Database-WFIntroducción

El trabajo con datos en dispositivos móviles se ha convertido ya en algo común y habitual en el desarrollo de aplicaciones. Existe una gran variedad de tipos de datos y formas de almacenamiento:

  • Archivos de texto. Texto plano o html cacheado en el espacio de almacenamiento aislado de la aplicación.
  • Imágenes. En el espacio de almacenamiento aislado de la aplicación o almacenadas en directorios conocidos del sistema.
  • Archivos serializados. Archivos XML o Json con objetos serializados.
  • Bases de datos. Cuando se requieren datos estructurados, obtener información más compleja con consultas avanzadas entre otro tipo de necesidades, la posibilidad de las bases de datos es la elección idónea.

En este artículo, vamos a conocer LiteDB y como utilizarlo con una aplicación Xamarin.Forms.

Recuerda

El mismo ejemplo que vemos en este artículo lo hemos visto previamente con:

Introducción a LiteDB

LiteDB es una base de da motor de base de datos Open Source  distribuido en una pequeña librería escrita en C# y compatible con .NET y .NET Standard. Inspirada en MongoDB almacena documentos BSON (Binary JSON).

Al ser compatible con .NET Standard 2.0, podemos utilizarla con Xamarin.iOS, Xamarin.Android y UWP con aplicaciones Xamarin.Forms.

Preparando el entorno

Comenzamos creando una aplicación Xamarin.Forms utilizando una librería .NET Standard:

Xamarin.Forms con librería .NET Standard

Tras crear la aplicación, añadimos las carpetas básicas para aplicar el patrón MVVM además del paquete NuGet de Autofac para la gestión del contenedor de dependencias.

Estructura del proyecto

Con el proyecto y estructura base creada, vamos a añadir LitrDB al proyecto. LiteDB esta disponible en NuGet. Vamos a añadir en cada proyecto de la solución la última versión disponible del paquete utilizando NuGet. El paquete a utilizar es LiteDB, implementación Open Source compatible con librerías .NET Standard.

LiteDB

Tras añadir la referencia vamos a crear una interfaz que defina como obtener la conexión con la base de datos y abstraer la funcionalidad específica de cada plataforma. Trabajando con LiteDB, el único trabajo específico a implementar en cada plataforma es determinar la ruta a la base de datos y establecer la conexión.

public interface IPathService
{
     string GetDatabasePath();
}

En Android, la implementación de IPathService nos permite establecer la conexión con la base de datos.

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), AppSettings.DatabaseName);
          if (!File.Exists(path))
          {
               File.Create(path).Dispose();
          }
          return path;
     }
}

NOTA: Utilizamos el atributo assembly:Dependency para poder realizar la resolución de la implementación con DependencyService.

En iOS, la implementación de IPathService nos permite establecer la conexión con la base de datos. El archivo de la base de datos lo situamos dentro de la carpeta Library dentro del espacio de almacenamiento de la aplicación.

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
          string libFolder = Path.Combine(docFolder, "..", "Library", "Databases");
          if (!Directory.Exists(libFolder))
          {
               Directory.CreateDirectory(libFolder);
          }
          return Path.Combine(libFolder, AppSettings.DatabaseName);
     }
}

Y por último en UWP:

public class PathService : IPathService
{
     public string GetDatabasePath()
     {
          return Path.Combine(ApplicationData.Current.LocalFolder.Path, AppSettings.DatabaseName);
     }
}

Todo listo para comenzar!

La definición de modelos

En nuestra aplicación, trabajaremos con elementos del listado ToDo, una única entidad sencilla.

public class TodoItem
{
     public int Id { get; set; }
     public string Name { get; set; }
     public string Notes { get; set; }
     public bool Done { get; set; }
}

La interfaz de usuario

En nuestra aplicación contaremos con dos vistas, un listado de tareas y una vista de detalles para crear, editar o eliminar una tarea específica.

Comenzamos definiendo la vista principal. Tendremos un listado de tareas:

<ListView ItemsSource="{Binding Items}"
     SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
     <ListView.ItemTemplate>
          <DataTemplate>
               <ViewCell>
                    <ViewCell.View>
                         <StackLayout Padding="20,0,20,0"
                              Orientation="Horizontal"
                              HorizontalOptions="FillAndExpand">
                              <Label Text="{Binding Name}"
                                   VerticalTextAlignment="Center"
                                   HorizontalOptions="StartAndExpand" />
                              <Image HorizontalOptions="End"
                                   IsVisible="{Binding Done}">
                                   <Image.Source>
                                        <OnPlatform x:TypeArguments="ImageSource">
                                             <On Platform="Android, iOS"
                                                  Value="check" />
                                             <On Platform="UWP"
                                                  Value="Assets/check.png" />
                                        </OnPlatform>
                                   </Image.Source>
                              </Image>
                         </StackLayout>
                    </ViewCell.View>
               </ViewCell>
          </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

A parte de definir como se visualizará cada elemento de la lista definiendo el DataTemplate establecemos la fuente de información, propiedad ItemsSource enlazada a propiedad de la ViewModel que obtendrá los datos de la base de datos.

Además del listado, debemos añadir en nuestra interfaz una forma de poder insertar nuevas tareas. Para ello, una de las opciones más habituales e idóneas es utilizar una Toolbar.

<ContentPage.ToolbarItems>
     <ToolbarItem Name="Add"
          Command="{Binding AddCommand}">
          <ToolbarItem.Icon>
                <OnPlatform x:TypeArguments="FileImageSource">
                     <On Platform="Android, iOS"
                         Value="plus" />
                     <On Platform="UWP"
                         Value="Assets/plus.png" />
                </OnPlatform>
          </ToolbarItem.Icon>
     </ToolbarItem>
</ContentPage.ToolbarItems>

Añadimos un ToolbarItem que permitirá añadir elementos.

La clase Device es muy importante en Xamarin.Forms ya que nos permite acceder a una serie de propiedades y métodos con el objetivo de personalizar la aplicación según dispositivo y plataforma. Además de permitirnos detectar el tipo de dispositivo, podemos detectar la plataforma gracias a la enumeración Device.OS o personalizar elementos de la interfaz gracias al método Device.OnPlatform entre otras opciones. En nuestro ejemplo, personalizamos el icono de añadir en base a la plataforma.

Nuestra interfaz:

Vista principal
Vista principal

Enlazamos la View con la ViewModel estableciendo una instancia de la ViewModel a la propiedad BindingContext de la página.

BindingContext = App.Locator.TodoListViewModel;

En la ViewModel contaremos con una propiedad pública para definir el listado de tareas, además de la tarea seleccionada (utilizada para la navegación):

private ObservableCollection<TodoItem> _items;
private TodoItem _selectedItem;

public ObservableCollection<TodoItem> Items
{
     get { return _items; }
     set
     {
          _items = value;
          OnPropertyChanged();
     }
}
public TodoItem SelectedItem
{
     get { return _selectedItem; }
     set
     {
          _selectedItem = value;
          OnPropertyChanged();
     }
}

Añadimos elementos con un comando disponible en la ViewModel.

private ICommand _addCommand;

public ICommand AddCommand
{
     get { return _addCommand = _addCommand ?? new Command(AddCommandExecute); }
}

private void AddCommandExecute()
{
}

Al pulsar y lanzar el comando, navegaremos a la vista de detalles.

var todoItem = new TodoItem();
_navigationService.NavigateTo<TodoItemViewModel>(todoItem);

Si creamos un nuevo elemento pasaremos como parámetro una nueva entidad de TodoItem, en caso de seleccionar una existente, pasaremos el seleccionado disponible en la propiedad SelectedItem.

Definimos la interfaz de la vista de detalles:

<StackLayout VerticalOptions="StartAndExpand"
     Padding="20">
     <Label Text="Name" />
     <Entry Text="{Binding Item.Name}" />
     <Label Text="Notes" />
     <Entry Text="{Binding Item.Notes}" />
     <Label Text="Done" />
     <Switch x:Name="DoneEntry"
          IsToggled="{Binding Item.Done}" />
     <Button Text="Save"
          Command="{Binding SaveCommand}" />
     <Button Text="Delete"
          Command="{Binding DeleteCommand}" />
     <Button Text="Cancel"
          Command="{Binding CancelCommand}" />
</StackLayout>

Añadimos cajas de texto para poder editar toda la información de una tarea además de botones para poder guardar, borrar o cancelar y navegar atrás.

El resultado:

Detalle
Detalle

Para enlazar la información de un elemento seleccionado, debemos capturar la información enviada en la navegación. Creamos una propiedad pública para enlazar con la UI de la tarea:

private TodoItem _item;

public TodoItem Item
{
     get { return _item; }
     set
     {
          _item = value;
          OnPropertyChanged();
     }
}

¿Cómo capturamos el elemento seleccionado en la navegación?. Utilizamos el método OnAppearing para capturar el parámetro NavigationContext.

public override void OnAppearing(object navigationContext)
{
     if (navigationContext is TodoItem todoItem)
     {
          Item = todoItem;
     }

     base.OnAppearing(navigationContext);
}

En cuanto a cada botón, cada uno de ellos estará enlazado a un comando:

Trabajando con LiteDB

Para trabajar con la base de datos utilizaremos DependencyService para obtener la implementación de IPathService y obtener una conexión.

var db = new LiteDatabase(DependencyService.Get<IPathService>().GetDatabasePath());

Para almacenar nuestras tareas, comenzamos creando la colección necesaria en la base de datos.

_collection = db.GetCollection<TodoItem>();

Si la colección no existe en la base de datos, es creada.

Cada elemento guardado en LiteDB cuenta con una propiedad Id utilizado para identificarlo de manera única. Debeemos indicarle a LiteDB qué propiedad de nuestro objeto actuará como identificador. Podemos hacerlo usando atributos o un mapeador. Aquí utilizaremos Mappers.

var mapper = BsonMapper.Global;

mapper.Entity<TodoItem>()
.Id(x => x.Id);

Para obtener el listado de elementos de una colección utilizaremos el método FindAll:

var all = _collection.FindAll();

A la hora de insertar, verificamos si estamos ante un registro existente o no, para realizar el registro de un nuevo elemento o actualizar uno existente con los métodos Insert o Update respectivamente.

var existingTodoItem = _collection.FindById(item.Id);

if (existingTodoItem == null)
_collection.Insert(item);
else
_collection.Update(item);

Eliminar es una acción sencilla realizada con el método Delete.

_collection.Delete(i => i.Id.Equals(item.Id));

Tenéis el código fuente disponible e GitHub:

Ver GitHub

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

Más información

[Xamarin.Forms] Novedades utilizando Resource Dictionaries

Resource Dictionaries

En toda aplicación móvil la apariencia visual es vital. Cada vez es mayor el esfuerzo depositado a la hora de crear aplicaciones atractivas a la par que intuitivas y en muchos casos conseguir una imagen única que diferencia a la Aplicación del resto es prioritario. Por este motivo, debemos de contar con opciones sencillas de poder personalizar los distintos elementos que componen la interfaz.

Los estilos permitir definir múltiples propiedades visuales de elementos de la interfaz de forma reutilizable.

Los recursos XAML son objetos que podemos reutilizar más de una vez. Hablamos desde un sencillo color o tamaño de fuente, a el uso de estilos. Los diccionarios de recursos o ResourceDictionaries permiten definir una zona donde definir recursos.

Hasta ahora…

Podíamos definir diccionarios de recursos en archivos totalmente independientes. Hablamos de una clase derivada de ResourceDictionary, que consta de un par de archivos. Por un lado tenemos el archivo XAML donde se definen los recursos y por otro lado, la clase subyacente encarga de realizar InitializeComponent.

NOTA: Para crear un archivo de tipo ResourceDictionary, crea una ContentPage o ContentView y renombra la clase base de ContentPage o ContentView a ResourceDictionary.

Veamos un ejemplo.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     x:Class="ResourceDictionaries.MyResourceDictionary">

     <Style x:Key="LabelStyle"
          TargetType="Label">
          <Setter Property="TextColor"
               Value="Red" />
          <Setter Property="FontSize"
               Value="24" />
</Style>

</ResourceDictionary>

NOTA: Fíjate que utilizamos x:Class para establecer la clase subyacente necesaria.

Para utilizar el diccionario de recursos, creamos una instancia:

<ContentPage.Resources>
     <local:MyResourceDictionary />
</ContentPage.Resources>

La forma anterior de añadir el diccionario de recursos a pesar de ser válida es muy limitada ya que sólo nos permite utilizar los recursos del diccionario instanciado. Podríamos necesitar utilizar mñas de un diccionario de recursos o añadir otros recursos.

Para solucionar este problema, tenemos disponible la posibilidad de utilizar diccionarios de recursos combinados.

<ContentPage.Resources>
     <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
               <local:MyResourceDictionary />
          </ResourceDictionary.MergedDictionaries>
     </ResourceDictionary>
</ContentPage.Resources>

Podemos combinar varias instancias de diccionarios de recursos en uno existente. Se puede realizar directamente desde XAML utilizando la propiedad MergedDictionaries.

Con la llegada de Xamarin.Forms 3.0

La combinación de ResourceDictionary se simplifica con la llegada de Xamarin.Forms 3.0. Por un lado, ya no es necesario utlizar las etiquetas MergedDictionaries ya que en su lugar, podemos agregar directamente otro ResourceDictionary utilizando la propiedad Source.

Además, Xamarin.Forms 3.0 crea automáticamente una instancia de ResourceDictionary por lo que ya no es necesario añadir estas etiquetas.

<ContentPage.Resources>
     <ResourceDictionary Source="MyResourceDictionary.xaml" />
</ContentPage.Resources>

Esta nueva sintaxis no crea una instancia del diccionario de recursos. Gracias a este cambio, ya no es necesario mantener la clase subyacente o code behind del diccionario de recursos (MyResourceDictionary.xaml.cs) por lo que podemos borrarlo además del atributo x:Class de la ráiz del diccionario.

Más simple y sencillo!

Puedes encontrar un ejemplo en GitHub:

Ver GitHub

Más información

[Material] Taller Xamarin en OpenSouthCode 2018

El evento

El pasado 01 y 02 de Juniose celebró en Málaga OpenSouthCode 2018, un evento gratuito para promocionar y dar a conocer las tecnologías abiertas: software/hardware libre y opensource.

El Material

He tenido la oportunidad de participar este año con un divertido taller de desarrollo con Xamarin.

El objetivo fue realizar una aplicación multiplataforma conXamarin.Formspara Android, iOS, UWP, macOS y Linux utilizando una Custom Vision. Tras una breve introducción donde repasamos conceptos básicos, comenzamos a crear la aplicación. Vimos una introducción al concepto de enlace a datos, MVVM y su aplicación, acceso a APIs nativas de cada plataforma y acabamos realizando peticiones HTTP para obtener información.

La aplicación realizada fue HotDotOrNot en inspiración de la serie “Silicon Valley”.

HotDotOrNot

Para acabar el taller nos relajamos con un divertido concurso de preguntas, algunos pequeños regalos, preguntas y networking.

Puedes encontrar la App realizada paso a paso en GitHub:

Ver GitHub

Más información

[Material DotNet2018] Xamarin.Forms Everywhere

El evento

El pasado 29 de Mayo tenía lugar la DotNet2018. Un gran día repleto de sesiones técnicas cubriendo todo el espectro de tecnologías .NET, desde Xamarin, Azure, IoT, VR, etc.

Fue un día genial de grandes sesiones pero también de encontrarte con grandes amigos, desvirtualizar a nuevos amigos, networking, etc. Quisiera agradecer a todos los ponentes y asistentes por hacer posible el evento. Y por supuesto, mi enhorabuena a toda la organización por hacer que algo tan grande y complejo fluyese de forma tan natural y sencilla.

El material

He tenido en placer de poder participar en el evento con una sesión acerca de Xamarin.Formsy nuevas plataformas o posibilidades.

Comenzamos el evento revisando Xamarin.Forms en Linux:

Xamarin.Forms en Linux

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Continuamos viendo estado y opciones de Xamarin.Forms en WPF:

Xamarin.Forms WPF

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Y continuamos con nuevos backends de Xamarin.Forms, y en esta ocasión hablamos de Ooui de Frank Krueger.

Desde el navegador!

Repasamos estado, un poco de WebAssembly y continuamos. Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHubContinuamos viendo los primeros pasos del backend de Windows Forms y dimos paso a nuevas posibilidades.

Hablamos de wearables y Tizen.CircularUI, donde repasamos opciones principales, etc.

Posteriormente llegço el turno de IoT. Tras una breve introducción a Xamarin.IoT vimos como ya contamos con plantillas de proyectos e integración entre Xamarin.Forms (backend GTK#) con Xamarin.IoT.

Monitor de temperatura realizado con Xamarin.IoT y Xamarin.Forms

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Tras ver nuevas plataformas o dispositivos con IoT o Wearables, nos centramos en nuevas posibilidades en UI. Hicimos un repaso de:

  • VisualStateManager
  • FexLayout
  • SkiaSharp
  • Etc

En este punto, vimos como crear interfaces combinadas con elementos 3Dcon Wave Engineo como combinar ARCorecon Xamarin.Forms creando Custom Renderers.

ARCore

Puedes encontrar el ejemplorealizado en GitHub:

Ver GitHub

Completamos el evento utilizando Xamarin.Forms para crear la UI de addins para Visual Studioen Windows y macOS.

El ejemplo:

Ver GitHub

Hasta el año próximo!

Y ya tenemos evento confirmado para el próximo año!. Apunta la fecha, 23 de Mayo.

DotNet 2019!

Allí nos vemos!

Más información

Xamarin Essentials

Introducción

Xamarin permite compartir código de lógica de negocio entre aplicaciones Android y iOS, incluso código relacionado con la interfaz de usuario utilizando Xamarin.Forms. Sin embargo, es habitual requerir acceder a APIs nativas de cada plataforma. En este caso es necesario crear código específico por plataforma. Gracias a la gran comunidad Xamarin existe una enorme variedad de Plugins.

Los Plugins nos facilitan el acceso a funcionalidad específica de cada plataforma sin necesidad de escribir grandes cantidades de código. Instalar un paquete NuGet, seguir documentación para inicialización y con pocas líneas se usa API multiplataforma para por ejemplo, verificar el estado de la red. Sin embargo, para poder hacer uso del Plugin tenemos que conocer el nombre del paquete, en ocasiones hay diferentes paquetes destinados al mismo objetivo, etc.

Xamarin.Essentials

Para hacerlo todo más sencillo, llega Xamarin.Essentials. Estamos ante un paquete oficial que podemos usar en nuestras aplicaciones Xamarin añadiendo acceso a APIs multiplataforma como uso de acelerómeto, red, batería o poder hacer llamadas.

Con soporte tanto a Xamarin tradicional como a Xamarin.Forms, actualmente cuenta con:

  • Accelerometer – Obtiene datos del acelerómetro.
  • App Information – Información de la App.
  • Battery – Detectar estado y nivel de la batería.
  • Clipboard – Copiar y pegar desde el Clipboard.
  • Compass – Monitorear cambios en la brújuja.
  • ConnectivityVerificar el estado de conectividad y detectar cambios.
  • Data Transfer – Enviar información a otras Apps.
  • Device Display InformationObtener las métricas y la orientación de la pantalla del dispositivo.
  • Device Information – Información del dispositivo.
  • Email – Enviar email.
  • File System Helpers – Forma sencilla de almacenar ficheros.
  • Flashlight – Forma sencilla de encender y apagar la linterna.
  • GeocodingGeocodificar direcciones y coordenadas.
  • Geolocation – Obtiene la localización del GPS..
  • GyroscopeRotación alrededor de los tres ejes principales del dispositivo.
  • MagnetometerDetecta la orientación del dispositivo en relación con el campo magnético de la Tierra.
  • Open Browser – Forma sencilla de abrir una web en el navegador.
  • Phone Dialer – Abre la App de teléfono.
  • Preferences – Sistema de preferencias rápido y sencillo.
  • Screen Lock – Mantiene la pantalla del dispositivo activa.
  • Secure Storage – Almacenamiento seguro.
  • SMS – Crea un SMS para enviarlo.
  • Text-to-Speech – Sintetizar texto en voz.
  • Version TrackingSeguimiento de la versión de la aplicación.
  • Vibrate – Hace que el dispositivo vibre.

NOTA: Los Plugins seguirán existiendo y aparecerán nuevos que cubran escenarios no cubiertos por Xamarin.Essentials. Contar con una vía oficial con soporte a las plataformas básicas de APIs fundamentales, es genial, pero no suprime los Plugins.

Las plataformas soportadas (en estos momentos) son:

  • Android 4.4 o superior
  • iOS 10.0 o superior
  • UWP 10.0.6299.0 o superior

Empezar a utilizar Xamarin.Essentials

Pasamos a ver como utilizarlo. Partimos de una aplicación nueva Xamarin.Forms. Vamos a añadir nuevo paquete NuGet:

Xamarin.Essentials NuGet

Buscamos por Xamarin.Essentials e instalamos el paquete en todos los proyectos, tanto librería .NET Standard como en Android, iOS y UWP.

Xamarin.Essentials requiere algo de código de inicialización por cada plataforma.

En Android, en la actividad principal vamos a incializar Xamarin.Essentials en el método OnCreate:

Xamarin.Essentials.Platform.Init(this, bundle);

Para poder gestionar en tiempo de ejecución el acceso a los permisos necesarios, debemos añadir también:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
     Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

     base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

No se requiere código especifico por plataforma en iOS o UWP.

¿Y a continuación?.

A continuación, tenemos todo preparado para tener acceso a cada una de las APIs listadas previamente.

Por ejemplo, para verificar si tenemos acceso a Internet:

using Xamarin.Essentials;

if (Connectivity.NetworkAccess != NetworkAccess.Internet) 
{
     // No internet access 
}

O para enviar un SMS:

using Xamarin.Essentials;

var message = new SmsMessage(messageText, recipient);
await Sms.ComposeAsync(message);

Al igual que Xamarin.Forms u otras grandes piezas relacionadas con Xamarin, Xamarin.Essentials es Open Source. Puedes utilizar GitHub para abrir una Issue o bien, puedes añadir acceso a una nueva API enviando una pull request.

Más información

Primer vistazo a Xamarin Live Reload

Introducción

Xamarin.Forms es un toolkit que crea una abstracción sobre la interfaz de usuariode Android, iOS, Tize, WPF, macOS, Linux y Windows permitiendo desarrollarla una única vez con códigoC#o Extensible Application Markup Language(XAML).

A la hora de trabajar con la interfaz de usuario, tenemos grandes herramientas como IntelliSense en XAML, Previewer o Xamarin Live Player. Sin embargo, las dos últimas opciones no soportan todas las características que se pueden utilizar en una aplicación móvil lo que provoca errores al renderizar y previsualizar el contenido. Por este motivo, tenemos algunas grandes herramientas por parte de la comunidad Xamarin comoLive XAMLo Gorilla Player.

En este artículo, vamos a conocer Xamarin Live Reload, nueva herramienta oficial que nos permite ver cualquier cambio de XAML al vuelo.

Xamarin Live Reload

El objetivo principal de Xamarin Live Reload es permitir ver cualquier cambio relacionado con la interfaz de usuario de forma rápida y sencillo, soportando cambios al vuelo sin necesidad de compilar y desplegar. Cualquier cambio en XAML será reflejado de forma automática manteniendo los cambios.

La clave fundamental de esta nueva herramienta es que soporta cualquier librería, control de terceros además de Custom Renderers o efectos. A la hora de ver la previsualización, podemos utilizar emuladores o dispositivos físicos.

Instalación

Para poder instalar Live Reload necesitamos los siguientes requisitos:

Bastará con descargar e instalar la siguiente extensión:

Instalar Xamarin Live Reload

Instalamos…

Live Reload

Tras instalar la herramienta es hora de configurar nuestra App para utilizarla. Es tarea sencilla ya que solo tendremos que añadir el paquete NuGet Xamarin.LiveReloada nuestra librería .NET Standard 2.0.

Xamarin.LiveReload

NOTA:Es necesario utilizar .NET Standard para utilizar Live Reload en estos momentos.

Para completar la configuración y utilizar la herramienta en nuestra aplicación, en la clase App.xaml.csdebemos añadir:

 LiveReload.Init();

En el punto de entrada de la misma, es decir, en el constructor.

Todo preparado!

Utilizando la herramienta

Tras crear un proyecto Xamarin.Forms y lanzarlo en depuración en el emulador vemos lo siguiente:

Comenzamos a usar Live Reload

Visual Studio nos indica (parte superior) que Live Reload esta funcionando en un emulador de Android. A partir de este momento, cualquier cambio será aplicado:

Live Reload en acción!

A tener en cuenta…

  • Live Reload funciona con XAML. Cualquier cambio en C# requiere recompilación.
  • Se soportan las plataformas soportadas por Xamarin.Forms.
  • No se soportan (por ahora) estilos CSS.
  • Sólo funciona utilizando librerías .NET Standard.
  • Se puede utilizar en emuladores o dispositivos.
  • No hay límite en el número de dispositivos a utilizar.
  • Si no se cambia la configuración de Live Reload ni tampoco la máquina donde se compiló el código, no es necesario desplegar de nuevo. Basta con abror la aplicación previamente desplegada, conectar y continuar.

Más información

[Tips and Tricks] Corregir “Xcode license must be accepted in order to be connected…”

El problema

Tras actualizar XCode a una versión posterior y desde entonces al intentar conectar desde Windows utilizando Xamarin Mac Agent…

Oops!

El mensaje de error nos lo deja todo muy claro, debemos abrir XCode y aceptar la neuva licencia. Sin embargo, tras abrir XCode, nunca salta la ventana para poder aceptar la licencia.

¿Qué ocurre?, ¿qué podemos hacer?.

La solución

Encontramos una sencilla solución al poder aceptar la licencia de XCode utilizando la línea de comandos.

Abre XCode y dirígete a Preferences > Locations. Asegúrate de tener establecida a la última versión de XCode la opción Command Line Tools:

Command Line Tools

Todo preparado. Abre una línea de comandos y ejecuta:

sudo xcodebuild -license

Acepta la licencia (agree), vuelve a Visual Studio en Windows e intenta de nuevo establecer una conexión. Todo debería funcionar sin problemas.

Más información