Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se explica cómo crear un representador personalizado para el control de mapa, que muestra un mapa nativo con una marca personalizada y una vista personalizada de los datos de marca en cada plataforma.
Todas las vistas de Xamarin.Forms tienen un representador que las acompaña para cada plataforma y que crea una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa un Map en iOS, se crea la instancia de la clase MapRenderer, que a su vez crea una instancia del control MKMapView nativo. En la plataforma de Android, la clase MapRenderer crea una instancia de un control MapView nativo. En la Plataforma universal de Windows (UWP), la clase MapRenderer crea una instancia de un elemento MapControl nativo. Para obtener más información sobre el representador y las clases de control nativo a las que se asignan los controles de Xamarin.Forms, vea Clases base y controles nativos del representador.
El siguiente diagrama muestra la relación entre la clase Map y los controles nativos correspondientes que la implementan:

El proceso de representación puede usarse para implementar las personalizaciones específicas de la plataforma creando un representador personalizado para una Map en cada plataforma. Para ello, siga este procedimiento:
- Cree un mapa personalizado de Xamarin.Forms.
- Consuma el mapa personalizado de Xamarin.Forms.
- Cree el representador personalizado para el mapa en cada plataforma.
Se explicará cada elemento uno por uno, para implementar un representador CustomMap que muestra un mapa nativo con una marca personalizada y una vista personalizada de los datos de marcas en cada plataforma.
Nota:
Xamarin.Forms.Maps tiene que inicializarse y configurarse antes de cada uso. Para obtener más información, vea Maps Control.
Crear un mapa personalizado
Se puede crear un control de mapa personalizado mediante la creación de subclases de la clase Map, como se muestra en el siguiente ejemplo de código:
public class CustomMap : Map
{
public List<CustomPin> CustomPins { get; set; }
}
El control CustomMap se crea en el proyecto de biblioteca de .NET Standard y define la API para el mapa personalizado. El mapa personalizado expone la propiedad CustomPins que representa la colección de objetos CustomPin que va a representar el control de mapa nativo en cada plataforma. La clase CustomPin se muestra en el siguiente ejemplo de código:
public class CustomPin : Pin
{
public string Name { get; set; }
public string Url { get; set; }
}
Esta clase define que un objeto CustomPin hereda las propiedades de la clase Pin y agrega las propiedades Name y Url.
Consumo del mapa personalizado
En XAML puede hacerse referencia al control personalizado CustomMap en el proyecto de biblioteca de .NET Standard declarando un espacio de nombres para su ubicación y usando el prefijo del espacio de nombres en el control de mapa personalizado. El siguiente ejemplo de código muestra cómo el control personalizado CustomMap puede utilizarse en una página XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<local:CustomMap x:Name="customMap"
MapType="Street" />
</ContentPage>
El prefijo de espacio de nombres local puede tener cualquier nombre. Empero, los valores deben clr-namespace y assembly coincidir con los detalles del mapa personalizado. Una vez que se declare el espacio de nombres, el prefijo se utiliza para hacer referencia al mapa personalizado.
El siguiente ejemplo de código muestra cómo el control personalizado CustomMap puede utilizarse en una página C#:
public class MapPageCS : ContentPage
{
public MapPageCS()
{
CustomMap customMap = new CustomMap
{
MapType = MapType.Street
};
// ...
Content = customMap;
}
}
La instancia CustomMap se usará para mostrar el mapa nativo en cada plataforma. La propiedad MapType establece el estilo de presentación del Map y los valores posibles que se definen en la enumeración MapType.
La ubicación del mapa y las marcas que contiene se inicializan como se muestra en el siguiente ejemplo de código:
public MapPage()
{
// ...
CustomPin pin = new CustomPin
{
Type = PinType.Place,
Position = new Position(37.79752, -122.40183),
Label = "Xamarin San Francisco Office",
Address = "394 Pacific Ave, San Francisco CA",
Name = "Xamarin",
Url = "http://xamarin.com/about/"
};
customMap.CustomPins = new List<CustomPin> { pin };
customMap.Pins.Add(pin);
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Distance.FromMiles(1.0)));
}
Esta inicialización agrega una marca personalizada y coloca la vista del mapa con el método MoveToRegion, que cambia la posición y el nivel de zoom del mapa mediante la creación de un MapSpan desde un Position y un Distance.
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para personalizar los controles de mapa nativos.
Creación del representador personalizado en cada plataforma
El proceso de creación de la clase de representador personalizada es el siguiente:
- Cree una subclase de la clase
MapRendererque represente el mapa personalizado. - Invalide el método
OnElementChangedque representa el mapa personalizado y escriba una lógica para personalizarlo. Se llama a este método cuando se crea el mapa personalizado de Xamarin.Forms correspondiente. - Agregue un atributo
ExportRenderera la clase de representador personalizada para especificar que se usará para representar el mapa personalizado de Xamarin.Forms. Este atributo se usa para registrar al representador personalizado con Xamarin.Forms.
Nota:
Proporcionar un representador personalizado en cada proyecto de la plataforma es un paso opcional. Si no se registra un representador personalizado, se usará el representador predeterminado de la clase base del control.
El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las relaciones entre ellos:

Las clases del representador específico de la plataforma, que se derivan de la clase MapRenderer para cada plataforma, representan el control CustomMap. Esto da como resultado que cada control CustomMap se represente con controles específicos de la plataforma, como se muestra en las siguientes capturas de pantalla:

La clase MapRenderer expone el método OnElementChanged, al que se llama cuando se crea el mapa personalizado de Xamarin.Forms para representar el control nativo correspondiente. Este método toma un parámetro ElementChangedEventArgs que contiene propiedades OldElement y NewElement. Estas propiedades representan al elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de Xamarin.Forms al que está asociado el representador, respectivamente. En la aplicación de ejemplo la propiedad OldElement será null y la propiedad NewElement contendrá una referencia a la instancia CustomMap.
El lugar para realizar la personalización de controles nativos es una versión reemplazada del método OnElementChanged en cada clase de representador específica de la plataforma. Una referencia con tipo para el control nativo que se usa en la plataforma puede obtenerse a través de la propiedad Control. Además, mediante la propiedad Xamarin.Forms se puede obtener una referencia al control de Element que se representa.
Debe tener cuidado al suscribirse a los controladores de eventos en el método OnElementChanged, como se muestra en el siguiente ejemplo de código:
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged (e);
if (e.OldElement != null)
{
// Unsubscribe from event handlers
}
if (e.NewElement != null)
{
// Configure the native control and subscribe to event handlers
}
}
Solo se debe configurar el control nativo y suscribir a los controladores de eventos cuando se adjunta el representador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado el presentador. Adoptar este enfoque facilita crear un presentador personalizado que no sufra pérdidas de memoria.
Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control personalizado de Xamarin.Forms que se representa y el nombre de tipo del representador personalizado. El prefijo assembly del atributo especifica que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación de cada clase de representador personalizado específico de plataforma.
Crear un representador personalizado en iOS
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En iOS la marca se denomina anotación y puede ser una imagen personalizada o una marca definida por el sistema de varios colores. Opcionalmente las anotaciones pueden mostrar una llamada, que se muestra en respuesta a que el usuario seleccione la anotación. La llamada muestra las propiedades Label y Address de la instancia Pin, con vistas adicionales a la derecha y a la izquierda. En la captura de pantalla anterior, la vista adicional izquierda es la imagen de un mono y la vista adicional derecha es el botón Información.
El ejemplo de código siguiente muestra el representador personalizado para la plataforma iOS:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.iOS
{
public class CustomMapRenderer : MapRenderer
{
UIView customPinView;
List<CustomPin> customPins;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
var nativeMap = Control as MKMapView;
if (nativeMap != null)
{
nativeMap.RemoveAnnotations(nativeMap.Annotations);
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView -= OnDidDeselectAnnotationView;
}
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;
nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView += OnDidDeselectAnnotationView;
}
}
// ...
}
}
El método OnElementChanged realiza la siguiente configuración de la instancia MKMapView, siempre que se adjunte el presentador personalizado a un nuevo elemento de Xamarin.Forms:
- La propiedad
GetViewForAnnotationse establece en el métodoGetViewForAnnotation. Se llama a este método cuando la ubicación de la anotación se vuelve visible en el mapa y se usa para personalizar la anotación antes de mostrarla. - Los controladores de eventos para los eventos
CalloutAccessoryControlTapped,DidSelectAnnotationViewyDidDeselectAnnotationViewse registran. Estos eventos se activan cuando el usuario pulsa el accesorio derecho de la llamada y cuando el usuario selecciona y anula la selección de la anotación, respectivamente. Se cancela la suscripción de los eventos solo cuando cambia el representador al que está adjunto el elemento.
Mostrar la anotación
Se llama al método GetViewForAnnotation cuando la ubicación de la anotación se vuelve visible en el mapa y se usa para personalizar la anotación antes de mostrarla. Una anotación tiene dos partes:
MkAnnotation: incluye el título, el subtítulo y la ubicación de la anotación.MkAnnotationView: contiene la imagen para representar la anotación y, opcionalmente, una llamada que se muestra cuando el usuario pulsa la anotación.
El método GetViewForAnnotation acepta un IMKAnnotation que contiene los datos de la anotación y devuelve un MKAnnotationView para su presentación en el mapa. Se muestra en el siguiente ejemplo de código:
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Name);
if (annotationView == null)
{
annotationView = new CustomMKAnnotationView(annotation, customPin.Name);
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Name = customPin.Name;
((CustomMKAnnotationView)annotationView).Url = customPin.Url;
}
annotationView.CanShowCallout = true;
return annotationView;
}
Este método garantiza que la anotación se muestre como una imagen personalizada, en lugar de como una marca definida por el sistema y que, cuando se pulsa la anotación, se muestre una llamada que incluye contenido adicional a la izquierda y a la derecha del título y la dirección de la anotación. Esto se logra de la siguiente manera:
- Se llama al método
GetCustomPinpara devolver los datos de marca personalizada para la anotación. - Para ahorrar memoria, la vista de la anotación se agrupa para volver a usarla con la llamada a
DequeueReusableAnnotation. - La clase
CustomMKAnnotationViewextiende la claseMKAnnotationViewcon las propiedadesNameyUrlque corresponden a las propiedades idénticas en la instanciaCustomPin. Se crea una nueva instancia de laCustomMKAnnotationView, siempre que la anotación seanull:- La propiedad
CustomMKAnnotationView.Imagese establece en la imagen que representará la anotación en el mapa. - La propiedad
CustomMKAnnotationView.CalloutOffsetse establece en unCGPointque especifica que la llamada se centrará por encima de la anotación. - La propiedad
CustomMKAnnotationView.LeftCalloutAccessoryViewse establece en una imagen de un mono que aparecerá a la izquierda del título y la dirección de la anotación. - La propiedad
CustomMKAnnotationView.RightCalloutAccessoryViewse establece en un botón Información que aparecerá a la derecha del título y la dirección de la anotación. - La propiedad
CustomMKAnnotationView.Namese establece en la propiedadCustomPin.Namedevuelta por el métodoGetCustomPin. Esto permite que la anotación pueda identificarse de forma que su llamada pueda personalizarse aún más si así lo desea. - La propiedad
CustomMKAnnotationView.Urlse establece en la propiedadCustomPin.Urldevuelta por el métodoGetCustomPin. La dirección URL se abrirá cuando el usuario pulse el botón que se muestra en la vista de accesorios de llamada correcta.
- La propiedad
- La propiedad
MKAnnotationView.CanShowCalloutse establece entruepara que se muestre la llamada cuando se pulsa la anotación. - La anotación se devuelve para su visualización en el mapa.
Seleccionar la anotación
Cuando el usuario pulsa en la anotación, se desencadena el evento DidSelectAnnotationView, que a su vez ejecuta el método OnDidSelectAnnotationView:
void OnDidSelectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
customPinView = new UIView();
if (customView.Name.Equals("Xamarin"))
{
customPinView.Frame = new CGRect(0, 0, 200, 84);
var image = new UIImageView(new CGRect(0, 0, 200, 84));
image.Image = UIImage.FromFile("xamarin.png");
customPinView.AddSubview(image);
customPinView.Center = new CGPoint(0, -(e.View.Frame.Height + 75));
e.View.AddSubview(customPinView);
}
}
Este método extiende la llamada existente (que contiene las vistas adicionales izquierdas y derechas) mediante la adición de una instancia de UIView a ella que contiene una imagen del logotipo de Xamarin, siempre que la anotación seleccionada tenga su propiedad Name establecida en Xamarin. Esto permite escenarios donde se pueden mostrar llamadas diferentes para distintas anotaciones. La instancia UIView se mostrará centrada por encima de la llamada existente.
Pulsar en la vista adicional de llamada derecha
Cuando el usuario pulsa el botón Información en la vista adicional de llamada derecha, se desencadena el evento CalloutAccessoryControlTapped, que a su vez ejecuta el método OnCalloutAccessoryControlTapped:
void OnCalloutAccessoryControlTapped(object sender, MKMapViewAccessoryTappedEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
if (!string.IsNullOrWhiteSpace(customView.Url))
{
UIApplication.SharedApplication.OpenUrl(new Foundation.NSUrl(customView.Url));
}
}
Este método abre un explorador web y navega a la dirección almacenada en la propiedad CustomMKAnnotationView.Url. Tenga en cuenta que la dirección se definió al crear la colección CustomPin en el proyecto de biblioteca de .NET Standard.
Anule la selección de la anotación
Cuando la anotación se muestra y el usuario pulsa en el mapa, se desencadena el evento DidDeselectAnnotationView, que a su vez ejecuta el método OnDidDeselectAnnotationView:
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
Este método garantiza que cuando la llamada existente no está seleccionada, la parte extendida de la llamada (la imagen del logotipo de Xamarin) también dejará de mostrarse y se liberarán sus recursos.
Para obtener más información sobre cómo personalizar una instancia de MKMapView, vea Maps in Xamarin.iOS (Mapas en Xamarin.iOS).
Crear un representador personalizado en Android
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En Android la marca se denomina marcador y puede ser una imagen personalizada o un marcador definido por el sistema de varios colores. Los marcadores pueden mostrar una ventana de información, que se muestra en la respuesta para el usuario que pulsa en el marcador. Muestra la ventana de información de las propiedades Label y Address de la instancia Pin y se pueden personalizar para incluir otro tipo de contenido. Con todo, solo una ventana de información puede mostrarse al mismo tiempo.
El siguiente ejemplo de código muestra el representador personalizado para la plataforma de Android:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.Droid
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
public CustomMapRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
}
}
protected override void OnMapReady(GoogleMap map)
{
base.OnMapReady(map);
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
}
...
}
}
Siempre que el representador personalizado esté asociado a un nuevo elemento de Xamarin.Forms, el método OnElementChanged recupera la lista de marcas personalizadas del control. Una vez que la instancia GoogleMap esté disponible, se invocará la invalidación OnMapReady. Este método registra un controlador de eventos para el evento InfoWindowClick, que se desencadena cuando se hace clic en la ventana de información y cuya suscripción solo se cancela cuando cambia el elemento al que está adjunto el representador. La invalidación OnMapReady también llama al método SetInfoWindowAdapter para especificar que la instancia de la clase CustomMapRenderer proporcionará los métodos para personalizar la ventana de información.
La clase CustomMapRenderer implementa la interfaz GoogleMap.IInfoWindowAdapter para personalizar la ventana de información. Esta interfaz especifica que se deben implementar los siguientes métodos:
public Android.Views.View GetInfoWindow(Marker marker): se llama a este método para devolver una ventana de información personalizada para un marcador. Si se devuelvenull, se usará la representación de la ventana predeterminada. Si se devuelve unView,Viewse colocará dentro del marco de la ventana de información.public Android.Views.View GetInfoContents(Marker marker): se llama a este método para devolver unViewque contiene el contenido de la ventana de información, y solo se llamará si el métodoGetInfoWindowdevuelvenull. Si devuelvenull, se usará la representación predeterminada del contenido de la ventana de información.
En la aplicación de ejemplo, solo se personaliza el contenido de la ventana de información, de forma que el método GetInfoWindow devuelve null para habilitar esto.
Personalización del marcador
El icono utilizado para representar un marcador puede personalizarse mediante una llamada al método MarkerOptions.SetIcon. Esto puede realizarse invalidando el método CreateMarker, que se invoca para cada Pin que se agrega al mapa:
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
return marker;
}
Este método crea una nueva instancia de MarkerOption para cada instancia de Pin. Después de establecer la posición, la etiqueta y la dirección del marcador, su icono se establece con el método SetIcon. Este método toma un objeto BitmapDescriptor que contiene los datos necesarios para representar el icono y la clase BitmapDescriptorFactory proporciona métodos auxiliares para simplificar la creación de la BitmapDescriptor. Para obtener más información sobre el uso de la clase BitmapDescriptorFactory para personalizar un marcador, vea Customizing a Marker (Personalización de un marcador).
Nota:
Si es necesario, el método GetMarkerForPin se puede invocar en el representador de mapa para recuperar un Marker de una Pin.
Personalización de la ventana de información
Cuando un usuario pulsa en el marcador, el método GetInfoContents se ejecuta, siempre que el método GetInfoWindow devuelva null. El siguiente ejemplo de código muestra el método GetInfoContents:
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Name.Equals("Xamarin"))
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
return null;
}
Este método devuelve un View con el contenido de la ventana de información. Esto se logra de la siguiente manera:
- Se recupera una instancia de
LayoutInflater. Esta se usa para crear una instancia de un archivo XML de diseño en suViewcorrespondiente. - Se llama al método
GetCustomPinpara devolver los datos de marca personalizada para la ventana de información. - Se aumenta el diseño de
XamarinMapInfoWindowsi la propiedadCustomPin.Namees igual aXamarin. En caso contrario, se aumenta el diseño deMapInfoWindow. Esto permite escenarios donde se pueden mostrar diferentes diseños de ventana de información para distintos marcadores. - Se recuperan los recursos
InfoWindowTitleyInfoWindowSubtitledesde el diseño aumentado y sus propiedadesTextse establecen en los datos correspondientes de la instancia deMarker, siempre que los recursos no seannull. - La instancia de
Viewse devuelve para su visualización en el mapa.
Nota:
Una ventana de información no es una View dinámica. En su lugar, Android convertirá la View a mapa de bits estático y la mostrará como una imagen. Esto significa que, mientras que una ventana de información puede responder a un evento de clic, no puede responder a los eventos de toque o gestos, y los controles individuales en la ventana de información no pueden responder a sus propios eventos de clic.
Hacer clic en la ventana de información
Cuando el usuario hace clic en la ventana de información, se desencadena el evento InfoWindowClick, que a su vez ejecuta el método OnInfoWindowClick:
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
Este método abre un explorador web y navega a la dirección almacenada en la propiedad Url de la instancia CustomPin recuperada para el Marker. Tenga en cuenta que la dirección se definió al crear la colección CustomPin en el proyecto de biblioteca de .NET Standard.
Para obtener más información sobre cómo personalizar una instancia de MapView, vea Using the Google Maps API in your application (Uso de la API de Google Maps en su aplicación).
Creación de un representador personalizado en la Plataforma universal de Windows
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En la UWP la marca se denomina icono de mapa y puede ser una imagen personalizada o la imagen predeterminada definida por el sistema. Un icono de mapa puede mostrar un UserControl, que se muestra en la respuesta para el usuario que pulsa en el icono de mapa. El UserControl puede mostrar cualquier contenido, incluyendo las propiedades Label y Address de la instancia Pin.
El siguiente ejemplo de código muestra el representador personalizado de UWP:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.UWP
{
public class CustomMapRenderer : MapRenderer
{
MapControl nativeMap;
List<CustomPin> customPins;
XamarinMapOverlay mapOverlay;
bool xamarinOverlayShown = false;
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
nativeMap.MapElementClick -= OnMapElementClick;
nativeMap.Children.Clear();
mapOverlay = null;
nativeMap = null;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
nativeMap = Control as MapControl;
customPins = formsMap.CustomPins;
nativeMap.Children.Clear();
nativeMap.MapElementClick += OnMapElementClick;
foreach (var pin in customPins)
{
var snPosition = new BasicGeoposition { Latitude = pin.Pin.Position.Latitude, Longitude = pin.Pin.Position.Longitude };
var snPoint = new Geopoint(snPosition);
var mapIcon = new MapIcon();
mapIcon.Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///pin.png"));
mapIcon.CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible;
mapIcon.Location = snPoint;
mapIcon.NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 1.0);
nativeMap.MapElements.Add(mapIcon);
}
}
}
...
}
}
El método OnElementChanged realiza las siguientes operaciones, siempre que se adjunte el presentador personalizado a un nuevo elemento de Xamarin.Forms:
- Borra la colección de
MapControl.Childrenpara quitar los elementos de interfaz de usuario existentes del mapa y después registra un controlador de eventos para el evento deMapElementClick. Este evento se desencadena cuando el usuario pulsa o hace clic en unMapElementen elMapControly solo se cancela su suscripción cuando cambia el elemento al que está adjunto el representador. - Cada marca en la colección de
customPinsse muestra en la ubicación geográfica correcta en el mapa como sigue:- La ubicación para la marca se crea como una instancia de
Geopoint. - Una instancia de
MapIconse crea para representar la marca. - La imagen utilizada para representar el
MapIconse especifica estableciendo la propiedadMapIcon.Image. Con todo, no siempre se puede garantizar que se muestre la imagen del icono de mapa, ya que puede estar ocultada por otros elementos del mapa. Por lo tanto, la propiedadCollisionBehaviorDesireddel icono del mapa se establece enMapElementCollisionBehavior.RemainVisible, para asegurarse de que está visible. - La ubicación del
MapIconse especifica configurando la propiedadMapIcon.Location. - La propiedad
MapIcon.NormalizedAnchorPointse establece en la ubicación aproximada del puntero en la imagen. Si esta propiedad conserva su valor predeterminado de (0,0), que representa la esquina superior izquierda de la imagen, los cambios en el nivel de zoom del mapa pueden dar lugar a que la imagen apunte a una ubicación distinta. - La instancia
MapIconse agrega a la colecciónMapControl.MapElements. Esto da como resultado que el icono de mapa se muestre en elMapControl.
- La ubicación para la marca se crea como una instancia de
Nota:
Cuando se usa la misma imagen para varios iconos de mapa, la instancia de RandomAccessStreamReference debe declararse en el nivel de página o aplicación para mejorar el rendimiento.
Mostrar el UserControl
El método OnMapElementClick se ejecuta cuando un usuario pulsa en el icono de mapa. El siguiente ejemplo de código muestra este método:
private void OnMapElementClick(MapControl sender, MapElementClickEventArgs args)
{
var mapIcon = args.MapElements.FirstOrDefault(x => x is MapIcon) as MapIcon;
if (mapIcon != null)
{
if (!xamarinOverlayShown)
{
var customPin = GetCustomPin(mapIcon.Location.Position);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Name.Equals("Xamarin"))
{
if (mapOverlay == null)
{
mapOverlay = new XamarinMapOverlay(customPin);
}
var snPosition = new BasicGeoposition { Latitude = customPin.Position.Latitude, Longitude = customPin.Position.Longitude };
var snPoint = new Geopoint(snPosition);
nativeMap.Children.Add(mapOverlay);
MapControl.SetLocation(mapOverlay, snPoint);
MapControl.SetNormalizedAnchorPoint(mapOverlay, new Windows.Foundation.Point(0.5, 1.0));
xamarinOverlayShown = true;
}
}
else
{
nativeMap.Children.Remove(mapOverlay);
xamarinOverlayShown = false;
}
}
}
Este método crea una instancia de UserControl que muestra información sobre la marca. Esto se logra de la siguiente manera:
- Se recupera la instancia de
MapIcon. - Se llama al método
GetCustomPinpara devolver los datos de marca personalizada que se mostrarán. - Se crea una instancia de
XamarinMapOverlaypara mostrar los datos de marca personalizada. Esta clase es un control de usuario. - Se crea la ubicación geográfica en la que se mostrará la instancia de
XamarinMapOverlayen elMapControlcomo una instancia deGeopoint. - La instancia
XamarinMapOverlayse agrega a la colecciónMapControl.Children. Esta colección contiene elementos de interfaz de usuario de XAML que se mostrarán en el mapa. - La ubicación geográfica de la instancia de
XamarinMapOverlayen el mapa se establece mediante una llamada al métodoSetLocation. - La ubicación relativa de la instancia de
XamarinMapOverlayque corresponde a la ubicación especificada se establece mediante una llamada al métodoSetNormalizedAnchorPoint. Esto garantiza que los cambios en el nivel de zoom del mapa tendrán como resultado que la instancia deXamarinMapOverlaysiempre se muestre en la ubicación correcta.
Como alternativa, si ya se muestra información sobre la marca en el mapa, pulsar en el mapa quita la instancia de XamarinMapOverlay de la colección de MapControl.Children.
Pulsar en el botón Información
Cuando el usuario pulsa el botón Información en el control de usuario de XamarinMapOverlay, se desencadena el evento Tapped, que a su vez ejecuta el método OnInfoButtonTapped:
private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}
Este método abre un explorador web y navega a la dirección almacenada en la propiedad Url de la instancia de CustomPin. Tenga en cuenta que la dirección se definió al crear la colección CustomPin en el proyecto de biblioteca de .NET Standard.
Para obtener más información sobre cómo personalizar una instancia de MapControl, vea Introducción a ubicación y mapas en MSDN.
Vínculos relacionados
- Xamarin.Forms Map (Mapa de Xamarin.Forms)
- Maps in Xamarin.iOS (Mapas en Xamarin.iOS)
- API de Maps