1. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Custom Render
Los Custom Render proporcionan un enfoque poderoso para personalizar la apariencia y el
comportamiento de los controles de Xamarin.Forms. Se pueden usar para pequeños cambios de estilo o
diseño sofisticado específico de la plataforma, así mismo como la personalización del comportamiento.
Xamarin.Forms utiliza abstracciones para definir los elementos. Posteriormente se transforma cada
abstracción ofreciendo una implementación y mecanismo en cada plataforma
Con la llegada de la versión 1.3 de Xamarin.Forms llegaron los estilos, los cuales nos ayudan a definir
múltiples propiedades visuales de elementos de la interfaz de forma reutilizable.
Por lo tanto, para modificaciones visuales simples no es necesario realizar ningún tipo de Custom Render.
Pero entonces, ¿cuándo son necesarios los Custom Render?
Para responder lo anterior, tenemos distintas opciones:
• Modificar la apariencia de elementos a niveles no posibles por estilos.
• Modificar el comportamiento de elementos existentes.
• Crear nuevos controles personales.
2. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Los elementos de Xamarin.Forms (páginas, layouts y controles), utilizan una API común que permite crear
interfaces visuales con el mismo código para todas las plataformas. Cada página, layout o control se
renderiza de forma diferente en cada plataforma. El esquema de que ocurre es el siguiente:
Todos los elementos de Xamarin.Forms se componen de dos partes diferenciadas, Element y Renderer.
Element: Es una clase que define al control, es decir, es el conjunto de propiedades y eventos que
permitirán gestionar tanto la apariencia, contenido y comportamiento del mismo. En el esquema superior
nos centramos en el elemento Button. La clase Element define el conjunto de propiedades de contenido
(Text), como las de apariencia (TextColor) y eventos.
Renderer: El elemento definido se renderiza (transforma) en cada plataforma a un elemento 100% nativo.
En el esquema anterior, la clase Renderer en cada plataforma creará un control nativo, UIButton en iOS y
Button en Android, asignando las definiciones que vienen establecidas desde el Element.
Ahora bien, el proceso para crear una clase de Custom Render es el siguiente:
• Crear una subclase de la clase del render que brinde el control nativo.
• Reemplazar el método que hace que el control nativo y la lógica de escritura personalicen el
control. A menudo, el método OnElementChanged se usa para representar el control nativo, que
se invoca cuando se crea el control Xamarin.Forms correspondiente.
• Agregar un atributo ExportRenderer a la clase del Custom Render para especificar que se usará
para representar el control Xamarin.Forms. Este atributo se usa para registrar el Custom Render
con Xamarin.Forms.
3. Ing. Vicente G. Guzman Lucio
Xamarin Developer
El siguiente código muestra un ejemplo de subclases del control Entry:
public class MyEntry : Entry
{
public MyEntry ()
{
BackgroundColor = Color.Gray;
}
}
El control MyEntry es un control de entrada donde BackgroundColor se establece en gris, y se puede hacer
referencia en XAML al declarar un espacio de nombres para su ubicación y usar el prefijo del espacio de
nombres en el elemento de control. El siguiente ejemplo de código muestra cómo un ContentPage puede
consumir el control personalizado MyEntry:
<ContentPage
...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<local:MyEntry Text="Xamarin Basic Concepts Code" />
...
</ContentPage>
El prefijo del espacio de nombres local puede ser cualquier cosa. Sin embargo, los valores de espacio de
nombre y ensamblaje deben coincidir con los detalles del control personalizado. Una vez que se declara
el espacio de nombres, el prefijo se utiliza para hacer referencia al control personalizado.
4. Ing. Vicente G. Guzman Lucio
Xamarin Developer
A continuación, conocerás el proceso para crear un Custom Render enfocado a visualizar Google Maps.
CODING TIME
Empecemos generando una nueva aplicación Cross Platform App (Xamarin).
Denominémosla como CustomRenders
Elijamos la platilla en blanco con las siguientes opciones: Xamarin.Forms – PCL
5. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Una vez creado nuestra solución, generemos las carpetas Controls y Views en nuestro proyecto Portable,
en donde agregaremos las clases relacionadas a las necesidades que soportara la aplicación. En Android
y iOS, solo agreguemos la carpeta Renders.
Antes Después
Primeramente, en Views agregaremos un ContenPage, la cual será nuestra pantalla de inicio de sesión
(LoginPage), después añadiremos una TabbedPage, que será la de la funcionalidad principal.
6. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Modifiquemos el archivo LoginPage, por el siguiente código:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomRenders.Views.LoginPage">
<ContentPage.Content>
<StackLayout VerticalOptions="StartAndExpand">
<Label Text="Username" />
<Entry x:Name="usernameEntry" Placeholder="username" />
<Label Text="Password" />
<Entry x:Name="passwordEntry" IsPassword="true" />
<Button Text="Login" Clicked="OnLoginButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Antes:
Después:
7. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Ahora en nuestro archivo .CS, agreguemos la navegación hacia nuestra pantalla de inicio (HomePAge)
async void OnLoginButtonClicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new HomePage());
}
Antes:
Después:
Nota: Si te aparece la pantalla de Welcome to Xamarin.Forms!, es que se te olvido algo…
8. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Tip – Es en el archivo App.xaml.cs
Después de realizar esto, en nuestro archivo de HomePage, en el Tab 2, agregaremos la funcionalidad
del mapa, pero antes de esto, es necesario, realizar lo siguiente:
Primero debemos instalar los Nugets: Xamarin.Forms.Maps y Xam.Plugin.Geolocator en todos los
proyectos (PCL, Android y iOS), para esto, solo hacemos click secundario en References (PCL) - Manage
Nuget Packages - Browse – Xamarin.Forms.Maps - Install
Ahora bien, en nuestro PCL, dentro de la carpeta Controls, agregaremos una nueva clase con el nombre
de CustomMap. Esta clase heredara de algún otro elemento que contenga ya el comportamiento o
aspecto visual similar al objetivo buscado, en nuestro caso, el mostrar un Mapa (Map).
Editemos la clase, de tal forma que quede así:
using Xamarin.Forms.Maps;
namespace CustomRenders.Controls
{
public class CustomMap : Map
{
public CustomCircle Circle { get; set; }
}
}
9. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Antes:
Después:
Al hacer esto, CustomCircle nos indica que no puede ser encontrado o que nos falta alguna referencia
que indique su existencia, para ello, en nuestra misma carpeta crearemos su clase.
La que llevara la información necesaria para indicar el posicionamiento de nuestro dispositivo.
using Xamarin.Forms.Maps;
namespace CustomRenders.Controls
{
public class CustomCircle
{
public Position Position { get; set; }
public double Radius { get; set; }
}
}
10. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Posteriormente, en nuestra carpeta de Renders del proyecto Android, agregaremos una clase
(CustomMapRenderer), la cual llevara toda la lógica del Mapa.
Antes de continuar, recordemos el último punto del proceso:
• Agregar un atributo ExportRenderer a la clase del Custom Render para especificar que se usará
para representar el control Xamarin.Forms. Este atributo se usa para registrar el Custom Render
con Xamarin.Forms.
Tomando en cuenta esto, deberemos de añadir el siguiente atributo
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
El cual va después de los “using”, y al cual le estamos definiendo el control del que hereda y el tipo.
Ya con esto, agreguemos el siguiente bloque de código:
/// <summary>
/// Render para los mapas personalizados
/// </summary>
public class CustomMapRenderer : MapRenderer
{
CustomCircle circle;
bool isDrawn;
protected override void
OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.Map
s.Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
11. Ing. Vicente G. Guzman Lucio
Xamarin Developer
circle = formsMap.Circle;
Control.GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender,
System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
var circleOptions = new CircleOptions();
circleOptions.InvokeCenter(new LatLng(circle.Position.Latitude,
circle.Position.Longitude));
circleOptions.InvokeRadius(circle.Radius);
circleOptions.InvokeFillColor(0x66ffff00);
circleOptions.InvokeStrokeColor(0x66ffff00);
circleOptions.InvokeStrokeWidth(0);
NativeMap.AddCircle(circleOptions);
isDrawn = true;
}
}
}
}
Ahora regresemos a nuestro archivo HomePage.xaml, al cual le declararemos un espacio de nombres para
poder ubicar al control:
xmlns:controls="clr-namespace:CustomRenders.Controls"
Posterior a esto, es necesario el acceder al prefijo antes declarado para así integrar nuestro control:
<controls:CustomMap
x:Name="customMap"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
IsShowingUser="true"
MapType="Street" />
12. Ing. Vicente G. Guzman Lucio
Xamarin Developer
XAML - HomePage
En nuestro .CS (HomePage.xaml.cs) deberemos de indicarle al mapa nuestra posición, la cual como
sabemos deriva de latitud y longitud, para poder realizar esto, creemos una carpeta el proyecto PCL que
se llame Models, el cual llevara una clase denominada Location, la que contara con lo siguiente:
namespace CustomRenders.Models
{
public class Location
{
public static double Latitude { get; set; }
public static double Longitude { get; set; }
}
}
13. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Contemplando lo anterior, editemos nuestro .CS de tal forma que quede semejante a este:
using CustomRenders.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Xaml;
namespace CustomRenders.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : TabbedPage
{
string location;
public HomePage()
{
InitializeComponent();
var pin = new Pin
{
Type = PinType.Place,
Position = new Position(Models.Location.Latitude,
Models.Location.Longitude),
Label = "Aqui estoy...",
Address = ""
};
location = $"{pin.Position.Latitude}|{pin.Position.Longitude}";
var position = new Position(Models.Location.Latitude,
Models.Location.Longitude);
customMap.Circle = new CustomCircle
{
Position = position,
Radius = 2500
};
customMap.Pins.Add(pin);
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(position,
Distance.FromMiles(1.0)));
}
}
}
Ya para casi finalizar, en nuestro archivo AndroidManifest que se encuentra dentro de Properties,
deberemos de agregar el siguiente meta-data, el cual indica el uso de los mapas de Google:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="########" />
Para adquirir el value, bastara con que naveguemos al portal:
https://developers.google.com/maps/documentation/static-maps/intro
14. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Y demos clic al botón de:
A continuación, solo seguiremos los pasos que se indiquen y después no arrojara la clave que
ingresaremos en el paso anterior.
En nuestro archivo App.xaml.cs indicaremos mediante el plugin de Geolocator que nos ubique con
respecto a nuestra posición actual, a través del siguiente código:
var maplocator = CrossGeolocator.Current;
maplocator.DesiredAccuracy = 500;
var geoCoder = new Geocoder(); Device.BeginInvokeOnMainThread(async () =>
{
try
{
if (!maplocator.IsListening)
{
15. Ing. Vicente G. Guzman Lucio
Xamarin Developer
await maplocator.StartListeningAsync(TimeSpan.FromMinutes(1), 50,
true);
}
}
catch (Exception ex)
{ }
});
maplocator.PositionChanged += (sender, e) =>
{
var position = e.Position;
Models.Location.Latitude = position.Latitude;
Models.Location.Longitude = position.Longitude;
};
Por último, no olvidemos habilitar los permisos en el AndroidManifest:
- ACCESS_COARSE_LOCATION
- ACCESS_FINE_LOCATION
- ACCESS_LOCATION_EXTRA_COMMANDS
- ACCESS_MOCK_LOCATION
- INTERNET
Y agregar la siguiente línea en nuestro MainActivity.cs
Xamarin.FormsMaps.Init(this, bundle);
16. Ing. Vicente G. Guzman Lucio
Xamarin Developer
Con esto habremos terminado, revisemos el resultado, ejecutemos…
Mas información: Entry Custom Renderer