Las extensiones de marcado son una forma de obtener un valor que no sea específico de tipo primitivo o un objeto XAML. Mediante la apertura y cierre de llaves, se define en su interior lo que se conoce como extensión de marcado.
En este artículo, vamos a conocer como crear nuestras propias extensiones de marcado.
RECUERDA: Ya vimos en un artículo anterior el concepto de extensión de marcado así como las extensiones de marcado disponibles.
Extensiones de marcado personalizadas
Las extensiones de marcado implementan la interfaz IMarkupExtension. Esta interfaz sólo cuenta con un método a implementar:
public object ProvideValue(IServiceProvider serviceProvider)
Cuando se realiza el análisis del código XAML y el compilador encuentra una extensión de marcado, se lanza este método antes de establecer el valor de la extensión.
Crear extensiones
Vamos a realizar una de las extensiones de marcado más sencillas posibles con el objetivo de centrarnos a fondo en los conceptos clave de la extensión. Nuestra extensión nos permitirá utilizar colores RGB estableciendo directamente los valores para rojo, verde y azul.
En el método ProvideValue, por defecto, no obtenemos valores con IServiceProvider. Por lo tanto, para obtener los valores necesarios en nuestra extensión (rojo, verde y azul) vamos a necesitar utilizar propiedades.
public class RGBColor : IMarkupExtension
{
public int Red { get; set; }
public int Green { get; set; }
public int Blue { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
return Color.FromRgb(Red, Green, Blue);
}
}
Creamos una clase que hereda de IMarkupExtension, implementamos el método ProvideValue que se lanzará a la hora de evaluar la expresión. Gracias a propiedades capturamos los valores deseados. La lógica del método ProvideValue se encarga de crear el valor devuelto deseado, un color.
Todo listo!
Utilizar extensiones
Utilizar la extensión es sencillo. Necesitamos acceder a la misma, por lo tanto, debemos declarar el espacio de nombres en XAML en el lugar a utilizar:
Fíjate que todo el código queda bastante simple y muy legible. Tenéis el código fuente del ejemplo utilizado disponible en GitHub:
Mediante la implementación de IMarkupExtension podemos crear nuestras propias extensiones de marcado. Las extensiones de marcado personalizadas nos dan una forma sencilla y potente de ejecutar código, realizar validaciones y conversiones de valores antes de establecer el valor de una propiedad.
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.
A la hora de trabajar con la interfaz en XAML con casi toda seguridad utilizarás alguna extensión de marcado.
Extensiones de marcado
Las extensiones de marcado son una forma de obtener un valor que no sea específico de tipo primitivo o un objeto XAML. Mediante la apertura y cierre de llaves, se define en su interior lo que se conoce como extensión de marcado.
Existen extensiones de marcados muy usadas y conocidas como Binding o StaticResource. Pero… ¿conoces todas las extensiones disponibles y su uso?.
En este artículo, vamos a conocer todas las extensiones básicas existentes.
Binding
Estamos ante la expresión de marcado más utilizada. Cuando el código XAML se encuentra con un literal entre llaves, lo interpreta y no se limita a mostrarlo como si fuera un simple texto. En este caso hace una evaluación encontrando la palabra reservada Binding, lo que indica que debe enlazar el valor de una propiedad. Pero…¿cómo sabe dónde se encuentra la propiedad?, todos los elementos que componen la interfaz de usuario descienden de una importante jerarquía de clases base que otorgan funcionalidad vital.
Contamos con la propiedad BindingContext que actúa como contexto de datos del elemento. Si la propiedad fuese nula en el elemento, se buscaría de forma ascendente en la jerarquía de elementos. Es decir, si la propiedad BindingContext del elemento visual es nula, buscará en el elemento padre inmediato, etc.
Veamos un ejemplo.
public class Person
{
public string Name { get; set; }
public string SurName { get; set; }
}
Establecemos el BindingContext:
BindingContext = person;
A la hora de utilizar el Binding:
<Label Text={Binding Name}" />
En un enlace a datos, la palabra reservada Mode indica la forma en la que se comporta el mismo. En Xamarin.Forms contamos con los siguientes modos:
OneWay: es el valor por defecto. Indica que el enlace se realiza en un único sentido, de lectura. Aunque el valor del elemento visual cambie, no viajará hasta el ViewModel.
OneWayToSource: en un único sentido generalmente de la View al ViewModel.
TwoWay: en este caso el enlace es bidireccional. La View toma el valor del ViewModel, pero si lo editamos en la vista, el cambio se envía de vuelta al ViewModel.
Default: el modo utilizado por defecto, que como hemos visto es OneWay.
<Entry Text={Binding Username, Mode=TwoWay}" />
Por otro lado, en ocasiones, lo que se desea mostrar en la interfaz de usuario no equivale al valor enlazado. Podríamos extender modelos añadiendo más propiedades para este propósito, pero no es lo ideal. Para solventar este problema contamos con las propiedades de formateo de texto.
<Label Text="{Binding Count, StringFormat='Number of Records = {0:N}'}"/>
Veamos otro ejemplo:
<Label Text="{Binding Date, StringFormat='{0:dd MM yyyy}'}" />
Converters
En ocasiones, el valor enlazado no es el deseado. Por ejemplo, tenemos enlazado un valor de tipo DateTime pero deseamos mostrar una cadena de texto al usuario con un formato de fecha aplicado. Justo para este objetivo, tenemos los TypeConverters.
En lugar de extender nuestros modelos con más y más valores, los Converters nos permiten transformar unos valores a otros directamente desde XAML.
<Label Text="{Binding Date}" />
En este caso veríamos el resultado de lanzar el método ToString del DateTime. Podemos crear un Converter:
public class DatetimeToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return string.Empty;
var datetime = (DateTime)value;
return datetime.ToLocalTime().ToString("g");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
El Converter es una clase que herede de la interfaz IValueConverter, se implementa el método Convert y ConvertBack. Para utilizar el converter se debe definir primero como recurso:
NOTA: Se suelen utilizar los Converters para transformaciones más complejas.
TemplateBinding
Con la versión 2.1.0 de Xamarin.Forms se introdujo el concepto de Control Templates y también el de TemplateBinding. Un TemplateBinding es similar a un Binding, excepto que el origen de un TemplateBinding siempre se establece automáticamente en el padre de la vista de destino que posee la plantilla de control.
En cada elemento visual podemos definir un conjunto de recursos. Utilizamos ResourceDictionary para a gestión de recursos. Con StaticResource podemos acceder a objetos estáticos definidos como recursos y disponibles en tiempo de compilación.
Esta extensión de marcado es sumamente similar a la anterior pero cuenta con una gran diferencia. DynamicResource permite acceder a recursos añadidos en tiempo de ejecución. Es decir, si un recurso es eliminado / añadido en un diccionario de recursos en tiempo de ejecución, DynamicResource es tu extensión.
var errorLabelStyle = new Style(typeof(Label));
errorLabelStyle.Setters.Add(new Setter()
{
Property = Label.BackgroundColorProperty, Value = Color.Red
});
this.Resources.Add(errorLabelStyle);
Tras añadir dinámicament el tiempo de ejecución el estilo, accedemos al mismo:
Extensión de marcado que permite establecer valor nulo. Si una propiedad cuenta con soporte a valores no nulos y se desea establecer por defecto a nulo, {x:Null} es la extensión de marcado idónea.
x:Reference
Los enlaces de datos se pueden definir para vincular propiedades de dos elementos visuales en la misma página.En este caso, se establece el BindingContext del objeto de destino utilizando la extensión de marcado x: Reference.
A pesar de lo que pueda dar a pensar, x:Static y StaticResource NO son iguales. Mientras que StaticResource nos permite acceder a un objeto definido en un diccionario de recursos, x:Static nos permite el acceso a una propiedad o constante estática.
NOTA: Esta extensión de marcado es más potente de lo que pueda parecer. También puedes acceder a campos o propiedades estáticas de tu propio código. Si se cuenta con una clase estática con constantes, se podrían utilizar.
x:Type
Permite establecer el tipo del objeto utilizando {x:Type Class}.
Extensiones de marcado personalizadas
Llegamos hasta aquí!. Cuando se comienza a desarrollar en XAML algunas son muy utilizadas y bien conocidas pero no así con todas. Xamarin.Forms también permite crear nuevas extensiones de marcado personalizadas aunque es algo que veremos en un próximo artículo.
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.
En ocasiones, se desarrollan diferentes aplicaciones para la misma empresa o marca comercial. Hablamos de aplicaciones totalmente diferentes pero que sin duda, van a compartir algunos elementos visuales específicos como colores, logos e incluso algunos estilos visuales (forma de botones, etc.).
¿Cómo lograr reutilizar estilos entre diferentes aplicaciones Xamarin.Forms?
ResourceDictionary
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.
Podemos definir recursos a nivel de elemento visual, a nivel de página e incluso a nivel de Aplicación.
¿Cómo definimos recursos en otra librería?.
Buscamos tener una librería con recursos, estilos y/o converters que sea compartida entre diferentes aplicaciones:
Librería
Aplicación 1
Aplicación 2
Creando una librería
Comenzamos creando una librería portable (PCL). En Visual Studio, nuevo proyecto de tipo PCL:
Añadimos el paquete NuGet de Xamarin.Forms. Y a continuación, añadimos una página de tipo ContentPage:
Renombramos ContentPage por ResourceDictionary en la página recien creada.
Todo listo!.
Bastará con añadir recursos, estilos o converters. Ejemplo:
Para utilizar la librería desde una aplicación Xamarin.Forms, comenzamos añadiendo la referencia a la librería desde la aplicación:
Añadida la referencia, llega el paso de mayor importancia. A nivel de Aplicación, tenemos la posibilidad de añadir recursos en un diccionario de recursos:
Realizamos una combinación de estilos a nivel de elemento visual, página, Aplicación e incluso desde otra librería (último botón). Tenéis el código fuente del ejemplo utilizado disponible en GitHub:
Recuerda, cualquier tipo de duda o sugerencia es bienvenida en los comentario del artículo.
Recientemente, en SVQXDG, tuvimos un interesante evento Xamarin relacionado con el rendimiento en Xamarin.Forms. Tras recibir feedback por diferentes redes sociales, meetup o vía correo, ante el interés en diferentes partes geográficas hemos decidido…repetir el evento online!
El evento
¿Sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentaremos dar solución en esta sesión online!.
La accesibilidad es un factor de vital importancia que por suerte, es tenida en cuenta cada vez con mayor frecuencia y peso en desarrollos web, móviles, etc.
Cuando hablamos de accesibilidad en el desarrollo, nos referimos al concepto de diseñar interfaces de usuario que funcionen de forma correcta ante las diferentes características de visualización y asistencia de entrada. La gestión de alto contraste, zoom, tamaño de fuente o la lectura de pantalla son opciones que permiten el acceso de muchos usuarios a nuestras aplicaciones.
Las principales plataformas móviles del mercado, es decir, iOS, Android y Windows cuentan con APIs para permitir a los desarrolladores crear aplicaciones accesibles.
Xamarin.Forms nos permite crear aplicaciones multiplataforma nativas compartiendo interfaces de usuario gracias a su capa de abstraccion.
¿Cómo creamos aplicaciones accesibles con Xamarin.Forms?
Soporte a accesibilidad en aplicaciones Xamarin.Forms
En versiones previas de Xamarin.Forms para añadir opciones de accesibilidad, requeríamos de Custom Renders. Acceder a APIs nativas para acceder a opciones de accesibilidad. Con la llegada de los efectos, todo se simplificó pemitiendo crear efectos con acceso a APIs nativas de forma más sencilla.
Con la llegada de Xamarin.Forms 2.3.5 se añade soporte directo a APIs de accesibilidad.
Opciones de accesibilidad
Xamarin.Forms incluye diferentes opciones de accesibilidad disponibles en la clase AutomationProperties y disponibles como propiedades adjuntas. Estas propiedades se encargan de añadir en cada plataforma el uso correcto de las diferentes opciones de accesibilidad.
AutomationProperties.HelpText
AutomationProperties.IsInAccessibleTree
AutomationProperties.LabeledBy
AutomationProperties.Name
HelpText
Es un valor de tipo cadena que se utilizará para sintetizar en voz.
En cada plataforma se utilizará una propiedad corespondiente a las APIs nativas:
Android: Hint
iOS: AccesibilityHint
Windows: AutomationProperties.HelpTextProperty
IsInAccesibleTree
Propiedad de tipo bool que indica si el elemento es accesible.
Esta propiedad permite que otro elemento visual defina información de accesibilidad. Así, podemos utilizar la información de otro elemento para ayudar a describir.
Internamente, cada plataforma utiliza las siguientes propiedades:
En general, a la hora de gestionar la accesibilidad en tu aplicación, deberíamos tener en cuenta los siguientes detalles:
Haz pruebas de tu interfaz con los esquemas de color en modo contraste alto.
Etiqueta los elementos de la interfaz con textos descriptivos utilizando las APIs de accesibilidad para permitir la lectura de la pantalla en cada plataforma.
Recuerda etiquetar también botones e imágenes de la aplicación con una descripción accesible.
Excluye elementos de decoración innecesarios!.
En muchas ocasiones las aplicaciones móviles gestionan diferentes estados sólo con elementos visuales. Por ejemplo, al realizar la carga de elementos, mostramos un indicador visual para indicar progreso y fin. Recuerda agregar en estos indicadores específicos de accesibilidad. También se pueden añadir refuerzos acústicos.
Ante contenido multimedia recuerda añadir descripciones legíbles y permitir acceder de forma sencilla a los botones de gestión del contenido.
Si la aplicación está localizada en diferentes idiomas, recuerda localizar también todas las descripciones de accesbilidad añadidas.
Cada plataforma cuenta con herramientas diferentes de narración. Android cuenta con opciones como Google TalkBack, iOS cuenta con VoiceOver y Windows con Narrator. Cada plataforma funciona de forma única a pesar de todas ellas contar con opciones similares. Es recomendable hacer pruebas en todas las plataformas donde nuestra aplicación puede funcionar.
En Xamarin.Forms, ¿sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentamos dar respuesta en el último evento de SVQXDG celebrado el pasado 24 de Mayo.
En cuanto a las demos técnicas realizadas, las tenéis disponible en GitHub:
Gracias a todos los asistentes al evento. En esta ocasión me ha sorprendido la enorme cantidad de feedback y peticiones relacionados con esta sesión. Pronto se anunciará su repetición en esta ocasión online!
Continuamos desde SVQXDGcon otro nuevo evento. En esta ocasión un tema muy tratado en diferentes quedadas y mencionado en eventos pero nunca hemos profundizado hasta ahora, el rendimiento en Xamarin.Forms.
¿Sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentaremos dar solución en esta sesión!.
La fecha
El evento tendrá lugar el próximo Miércoles, 24 de Mayo de 19:00h a 20:30h. Tendremos una única sesión técnica de 90 minutos de duración. Además contaremos con algún detalle entre los asistentes.
Recientemente se liberaba la versión 2.3.5 de Xamarin.Forms donde entre las diferentes novedades, destaca la aparición de FastRenderers y la mejora de rendimiento. Por ahora, sólo disponible en Android e incluido en los controles Label, Button, Frame e Image. Pero.. ¿qué son los Fast Renderers?, ¿por qué se habla de mejora de rendimiento?.
Fast Renderers
Hablamos de cambios realizados en Xamarin.Forms con el objetivo de reducir a mínimos de operaciones y cálculos a realizar para renderizar el control y gestionar su tamaño y posición.
Por un lado se ha simplificado notablemente el conjunto de clases implicadas para realizar los cálculos necesarios para el renderizado, tamaño y posición. Previamente, un Label en Xamarin.Forms al ejecutarse en Android realizaba:
Se reduce el número de participantes llegando de forma mucho más directa al renderizado. Se sigue implementando la interfaz IVisualElementRenderer que permite tener los métodos OnElementChanged y OnElementPropertyChanged.
Hasta este punto, a pesar de reducir piezas en la lógica, no se ve una reducción drástica en el ciclo del Layout. También se han realizado importantes modificaciones en el ciclo del Layout. Previamente:
OnLayout de la clase LabelRenderer
OnLayout de ViewRenderer
MeasureAndLayout de ViewRenderer
OnLayout de VisualElementRenderer
UpdateLayout del control
Ahora:
On Layout de la clase LabelRenderer
OnLayout View
Además, mientras que antes todos los parámetros del Layout se actualizaban (OJO: Opacity, Rotation, Scale, etc) ahora se establecen todas una vez. Tambien se añaden validaciones para evitar refrescos del Layout innecesarios (Invalidate).
NOTA: Se mantienen también los renderers anteriores de los controles Label, Image, Frame y Button por compatibilidad hacia atrás.
Comparativa de rendimiento
Dependiendo del Layout y controles utilizados en la aplicación, la mejora de rendimiento se notará en mayor o menor medida. Hablamos desde un pequeño porcentaje hasta en según que ocasiones practicamente duplicar el rendimiento.
Para profundizar en este aspecto, vamos a crear un ejemplo con dos pruebas. Realizaremos una prueba básica donde tendremos una vista con 50 Buttons y 50 Labels. Recuerda, actualmente se han añadido FastRenderers de ButtonRenderer, FrameRenderer, LabelRenderer y ImageRenderer. La segunda prueba será un listado con cientos de elementos donde la plantilla que define cada fila utilizará un control de tipo Image, y dos Labels.
El ejemplo se encuentra disponible en GitHub:
Se utiliza Stopwatch para realizar medidas de tiempo de renderizado además de pruebas con la experiencia al hacer scroll en el listado, etc. Cambiando el paquete de Xamarin.Forms de versiones previas a la versión 2.3.5 o superior podemos hacer diferentes comparativas. Se aprecian mejores resultados en los tiempos (946ms > 708ms en el ejemplo básico). Te animo a descargar el ejemplo y a realizar tus propias medidas.
No cabe duda que en futuras versiones de Xamarin.Forms nos esperan más y más renderers utilizando FastRenderers lo que al final, nos afecta como desarrolladores, en una ganancia de rendimiento sin necesidad de trabajo extra.
Xamarin.Forms ofrece una capa de abstracción sobre la capa de la interfaz de usuario permitiendo definir la interfaz de usuario una única vez con código C# o XAML.
Desde los inicios de Xamarin.Forms a hoy día, todo ha evolucionado a pasos agigantados tanto las propias posibilidades de Xamarin.Forms, como el crecimiento de la comunidad así como la implicación de terceros. Hoy precisamente nos vamos a centrar en la segunda versión de Grial UI Kit.
NOTA: Gracias a los chicos de UXDivers por ceder una licencia de Grial para realizar pruebas sobre la librería.
Grial UI Kit
El objetivo principal de Grial es ofrecer un conjunto de controles y plantillas para Xamarin.Forms permitiendo crear interfaces de usuario ricas y vistosas con facilidad.
Cuenta con soporte para:
Android 4.1+ (API Level 16).
iOS 8.0+.
Incluye:
Plantillas y vistas preparadas
Controles
Efectos
Animaciones
Temas
Iconos
Novedades
De la versión anterior a ahora se han añadido nuevas plantillas de páginas, nuevos controles, tres nuevos temas, animaciones y soporte a tabletas.
Creación de proyectos
La primera de las novedades visibles que vemos esta relacionada con la creación de proyectos. Accedemos a un portal de administración llamado Grial Admin.
Desde el panel de administración podemos crear aplicaciones añadiendo el nombre. De esta forma podemos descargar dos tipos de proyectos:
Full: Contiene absolutamente todos los ejemplos y controles incluidos en Grial.
Starter: Proyecto mínimo que cuenta con soporte a Grial, sus librerías.
Ambos cuentan con el espacio de nombres y todo lo necesario ya preparado con el nombre asignado a la aplicación.
Tras descargar el proyecto, debemos configurar el acceso a los paquetes NuGet añadiendo un nuevo repositorio de paquetes.
En Visual Studio, accedemos al menu de Opciones > Herramientas > Nuget Package Manager > Package Sources.
NOTA: Para configurar el repositorio será necesario introducir usuario y contraseña.
Optimización a Tablets
Una de las novedades más importantes se encuentra en las posibilidades de adaptación a diferentes factores de forma, teléfonos y tabletas.
Además de tener plantillas ya preparadas para funcionar correctamente tanto en teléfonos como tabletas, se añade un unos helpers Responsive.
Este helper cuenta con diferentes propiedades:
Default: Aplica a todos los casos. Es el valor por defecto.
Portrait: Aplica a todos los dispositivos en modo vertical.
Landscape: Aplica a todos los dispositivos en modo horizontal.
PortraitPhone: Aplica solo a teléfonos en modo vertical.
PortraitTablet: Aplica solo a tabletas en modo vertical.
PortraitDesktop: Aplica solo cuando el dispositivo es escritorio en modo vertical.
LandscapePhone: Aplica en teléfonos en modo horizontal.
LandscapeTablet: Aplica en tabletas en modo horizontal.
LandscapeDesktop: Aplica en escritorio en modo horizontal.
El texto mostrará un texto diferente en modo vertical y horizontal además, siempre será visible menos en modo vertical en escritorio. Añade de forma sencilla bastante versatilidad para adaptar la interfaz de usuario en diferentes modos.
Temas
Se añaden varios temas ya preparados y tareas de compilación que permiten cambiar temas al vuelo en la aplicación.
Nuevos controles
Se han añadido nuevos controles:
TabControl: Control de pestañas compatible con iOS y Android que permite el posicionamiento tanto superior como inferior. Totalmente personalizable.
Badge: Icono numérico.
CircleIcon: Imagen circular.
Timeline: Listado con formato línea del tiempo.
Rating: Control para puntuar con estrellas.
Repeater: Control de listados con personalización de scroll, tamaño de elementos, etc.
FontIcons: Permite mostrar iconos utilizando fuente. Incluidos más de 1500 iconos.
Lottie Animations: Se incluye soporte a animaciones Lottie.
Analizando Grial
Tras ver novedades principales, vamos a continuar realizando un pequeño análisis de la librería.
El tamaño del paquete
Grial se basa en un conjunto de diferentes librerías.
PCL
UXDivers.Artina.Shared.dll (50KB)
UXDivers.Artina.Shared.Base.dll (13KB)
iOS
UXDivers.Artina.Shared.iOS.dll (19KB)
UXDivers.Artina.Shared.Base.iOS.dll (12KB)
Android
UXDivers.Artina.Shared.Droid.dll (31KB)
UXDivers.Artina.Shared.Base.Droid.dll (14KB)
Para utilizar los efectos tenemos otra librería:
PCL
UXDivers.Effects.dll (7KB)
iOS
UXDivers.Effects.iOS.dll (8KB)
Android
UXDivers.Effects.Droid.dll (93KB)
Y para controles:
PCL
UXDivers.Artina.Shared.Tab.dll (20KB) – Control TabControl
UXDivers.Artina.Shared.Repeater.dll (17KB) – Control Repeater
Dependencias
Con el firme objetivo de mejorar el rendimiento se han añadido algunas dependencias con librerías de terceros con respecto a la versión anterior. Nos encontramos con:
El XAML aportado en el proyecto PCL no está muy acoplado, sólo con los estilos definidos en App.xaml. En el proyecto Xamarin.Android, tenemos una personalización de temas nativos. Mientras que en el proyecto Xamarin.iOS, esta personalización de temas viene en forma de un archivo ThemeColors.cs.
Rendimiento
Grial hace uso de compilación de XAML, reutilización de celdas en listados y otras características incluidas en Xamarin.Forms centradas en el rendimiento. Tras hacer pruebas en emuladores y dispositivos haciendo medidas con Xamarin Profiler, no se encuentran problemas ni penalizaciones relacionadas con el rendimiento.
MVVM
Las páginas con diferentes plantillas, layouts y controles se encuentran asociadas a una ViewModel. La implementación del patrón así como la separación de responsabilidades se encuentra bien implementada ayudando a su correcta implementación al utilizar Grial. Los controles añadidos como por ejemplo el TabControl, añaden propiedades de dependencia necesarias para controlar el comportamiento y apariencia del control.
Soporte multiplataforma
Contamos con soporte a iOS y Android. Tras múltiples pruebas en diferentes dispositivos y emuladores nos encontramos con interfacecs robustas con una implementación prácticamente exacta en cada plataforma y condición.
No encontramos eso sí soporte a UWP (Universal Windows Platform). Es una plataforma que permite el acceso a los diferentes dispositivos de familias de Windows 10 (PCs, tabletas, teléfonos, Xbox One, Surface Hub, IoT, HoloLens).
Conclusiones
Volvemos a estar frente a un paquete bastante grande de plantillas con vistas muy comunes y necesarias en muchas aplicaciones (vista detalle de producto, sign in, registro, chat, configuración, etc.) con un diseño muy cuidado y adaptado a diferentes plataformas y factores de forma. En esta versión nos encontramos la adaptación a diferentes factores de forma, el uso de temas o la llegada de nuevos controles.
Un componente que no solo aporta funcionalidad, también la forma de hacer ciertas necesidades en la UI con Xamarin.Forms.
En el desarrollo de aplicaciones móviles contamos con una serie de controles de peso, altamente utilizados y que definen las características básicas de muchas aplicaciones. Hablamos de controles como listados, carruseles o mapas.
En Xamarin.Forms se pueden utilizar mapas utilizando las APIs nativas de cada plataforma. El uso básico se ve altamente simplifcado utilizando un paquete NuGet, Xamarin.Forms.Maps, que requiere cierta configuración inicial.
Trabajando con mapas, el inicio
Para trabajar con mapas en Xamarin.Forms debemos de comenzar añadiendo un paquete NuGet en cada proyecto de la solución, es decir, tanto en la librería portable como en cada proyecto nativo.
Tras añadir el paquete NuGet se deben de realizar tareas de inicialización.
En el caso de iOS se debe añadir:
Xamarin.FormsMaps.Init();
En el delegado principal de la aplicación tras inicializar Xamarin.Forms en el método FinishedLaunching.
En el caso de Android:
Xamarin.FormsMaps.Init(this, bundle);
Se realiza la inicialización de los mapas en la actividad principal utilizando el método OnCreate.
Por último, en el caso de Windows, la inicialización se realiza en el constructor de la página principal, MainPage:
Además de realizar el proceso de inicialización, los mapas requieren de ciertas capacidades además de requerir un Api Key correspondiente a los mapas nativos utilizados en cada plataforma.
En iOS, si se utiliza iOS 8 o superior, es necesario añadir dos claves al Info.plist.
<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>
En Android se utiliza la API de Google Maps que requiere un Api Key. A nivel de aplicación en el arhivo de manifiesto Android es necesario añadir:
Además, es necesario añadir los siguientes permisos:
AccessCoarseLocation
AccessFineLocation
AccessLocationExtraCommands
AccessMockLocation
AccessNetworkState
AccessWifiState
Internet
Se pueden añadir desde Opciones > Build > Android Application.
Para trabajar con Windows, como ya vimos en el proceso de inicialización, se necesita generar un token de autorización para poder autenticar con Bing Maps.
Uso básico de mapas
A la hora de trabajar con mapas en Xamarin.Forms haremos uso del control Map. El control se puede inicializar y utilizar desde C#:
var map = new Map(
MapSpan.FromCenterAndRadius(
new Position(37,-122), Distance.FromMiles(0.3))) {
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 960,
VerticalOptions = LayoutOptions.FillAndExpand
};
MapType: Permite establecer el tipo de mapa entre vista satélite, calle e híbrida.
Pins: Conjunto de pushpins del mapa.
Para añadir pushpins al mapa basta con rellenar la colección Pins con objetos de tipo Pin.
En cuanto a los métodos fundamentales tenemos:
MoveToRegion: Permite establecer y modificar la posición y nivel de zoom aplicado al mapa.
MyMap.MoveToRegion(
MapSpan.FromCenterAndRadius(
new Position(37,-122), Distance.FromMiles(1)));
Utilizando MVVM
Hasta este punto tenemos el mapa funcionando y contamos con todos los conceptos básicos necesarios para trabajar con mapas. Sin embargo, rellenar la colección de pushpins utilizando la propiedad Pins o posiciona el mapa utilizando el método MoveToRegion en el código asociado, no es lo que habitualmente realizamos.
¿Cómo utilizamos todo utilizando MVVM?
Pongámonos en situación. Habitualmente tendremos cierto contexto. Imagina que tenemos una aplicación de Taxis donde por supuesto, tendremos clientes. Cada cliente lo representaremos utilizando una entidad, dentro de la carpeta Models:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public string Description { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
public DateTime? FullyAttendedTime { get; set; }
}
La lógica necesaria para añadir pushpins al mapa es sencilla utilizando la propiedad Pins del mapa. También podemos gestionar la posición central del mapa y el nivel de zoom utilizando el método MoveToRegion:
private void AddPins()
{
foreach (var customer in DataRepository.LoadCustomerData())
{
var pin = new Pin
{
Type = PinType.Place,
Position = new Position(customer.Latitude, customer.Longitude),
Label = customer.Name,
Address = customer.Address
};
MyMap.Pins.Add(pin);
}
}
private void PositionMap()
{
MyMap.MoveToRegion(
MapSpan.FromCenterAndRadius(
new Position(GlobalSetting.UserLatitude, GlobalSetting.UserLongitude),
Distance.FromMiles(1)));
}
Vamos a aprovechar toda la versatilidad y potencia que nos brindan los Behaviors en Xamarin.Forms.
El concepto de Behavior es algo muy sencillo. Un Behavior espera por “algo” para hacer “algo”. Concretamos más. Un Behavior espera por “algo”. Puede ser un evento que se lanza, el cambio de una propiedad o cualquier otra acción personalizada que deseamos monitorear. Una vez que ese “algo” se desencadena, el Behavior puede hacer acciones muy variadas, desde cambiar el valor de una propiedad, lanzar un evento, hacer verificaciones o validaciones, etc.
Los Behaviors nos permiten encapsular lógica que se puede adjuntar a un componente específico. Generalmente ayudan a personalizar o completar ciertos componentes e incluso en muchas ocasiones son un a ayuda fundamental para mantener una estructura idónea al implementar patrones como MVVM.
Comenzamos creando la clase MapBehavior que debe de heredar de BindableBehavior<Map>:
public class MapBehavior : BindableBehavior<Map>
{
}
Para permitir tener acceso a la colección de pushpins vamos a crear una BindableProperty de tipo IEnumerable<Customer>:
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.CreateAttached("ItemsSource", typeof(IEnumerable<Customer>), typeof(MapBehavior),
default(IEnumerable<Customer>), BindingMode.Default, null, OnItemsSourceChanged);
public IEnumerable<Customer> ItemsSource
{
get { return (IEnumerable<Customer>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
Al recibir la información, añadiremos los pushpins al mapa y centraremos la posición deseada:
private static void OnItemsSourceChanged(BindableObject view, object oldValue, object newValue)
{
var mapBehavior = view as MapBehavior;
if (mapBehavior != null)
{
mapBehavior.AddPins();
mapBehavior.PositionMap();
}
}
Sencillo, ¿cierto?. Pues ahora llega el momento de utilizar el Behavior. Añadimos en la página el namespace XAML necesario:
Tenéis el código fuente del ejemplo utilizado disponible en GitHub:
Hasta este punto hemos aprendido como utilizar mapas en Xamarin.Forms de forma básica. Sin embargo, en las aplicaciones que hacen un uso intensivo de mapas vemos características como:
Pushpins totalmente personalizados.
Pushpins dinámicos (se añaden, se quitan e incluso se desplazan).
Mostrar un diálogo al pulsar un puhspin.
Traza de rutas.
Dibujo de figuras poligonales.
¿Cómo hacemos estas opciones en Xamarin.Forms?. En próximos artículos iremos profundizando en el uso de mapas viendo como realizar cada uno de los puntos anteriores.
Recuerda, cualquier tipo de duda o sugerencia es bienvenida en los comentario del artículo.