Personalizzazione di un segnaposto per la mappa
Questo articolo illustra come creare un renderer personalizzato per il controllo mappa che visualizza una mappa nativa con un segnaposto personalizzato e una visualizzazione personalizzata dei dati del segnaposto in ogni piattaforma.
Ogni Xamarin.Forms visualizzazione ha un renderer a discesa per ogni piattaforma che crea un'istanza di un controllo nativo. Quando un Map
oggetto viene sottoposto a rendering da un'applicazione Xamarin.Forms in iOS, viene creata un'istanza della MapRenderer
classe , che a sua volta crea un'istanza di un controllo nativo MKMapView
. Nella piattaforma Android la classe MapRenderer
crea un'istanza di un controllo MapView
nativo. Nella piattaforma UWP (Universal Windows Platform) la classe MapRenderer
crea un'istanza di un controllo MapControl
nativo. Per altre informazioni sulle classi di controllo native e del renderer a cui Xamarin.Forms viene eseguito il mapping dei controlli, vedere Classi di base del renderer e controlli nativi.
Il diagramma seguente illustra la relazione tra la classe Map
e i controlli nativi corrispondenti che la implementano:
È possibile usare il processo di rendering per implementare personalizzazioni specifiche della piattaforma creando un renderer personalizzato per un elemento Map
in ogni piattaforma. Il processo per eseguire questa operazione è il seguente:
- Creare una Xamarin.Forms mappa personalizzata.
- Utilizzare la mappa personalizzata da Xamarin.Forms.
- Creare il renderer personalizzato per la mappa in ogni piattaforma.
Verrà descritto ogni elemento per implementare un renderer CustomMap
che visualizza una mappa nativa con un segnaposto personalizzato e una visualizzazione personalizzata dei dati del segnaposto in ogni piattaforma.
Nota
Xamarin.Forms.Maps
deve essere inizializzato e configurato prima dell'uso. Per ulteriori informazioni, vedere Maps Control
.
Creazione della mappa personalizzata
Per creare un controllo mappa personalizzato, è possibile creare una sottoclasse della classe Map
, come illustrato nell'esempio di codice seguente:
public class CustomMap : Map
{
public List<CustomPin> CustomPins { get; set; }
}
Il controllo CustomMap
viene creato nel progetto di libreria .NET Standard e definisce l'API per la mappa personalizzata. La mappa personalizzata espone la proprietà CustomPins
che rappresenta la raccolta di oggetti CustomPin
di cui il controllo mappa nativa eseguirà il rendering in ogni piattaforma. La classe CustomPin
è illustrata nell'esempio di codice seguente:
public class CustomPin : Pin
{
public string Name { get; set; }
public string Url { get; set; }
}
Questa classe definisce un oggetto CustomPin
come che eredita le proprietà della Pin
classe e l'aggiunta Name
di proprietà e Url
.
Utilizzo della mappa personalizzata
Per fare riferimento al controllo CustomMap
in XAML nel progetto di libreria .NET Standard, è possibile dichiarare uno spazio dei nomi per il percorso e usare il prefisso dello spazio dei nomi nel controllo mappa personalizzato. Nell'esempio di codice riportato di seguito viene illustrato come il controllo CustomMap
può essere usato da una pagina XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<local:CustomMap x:Name="customMap"
MapType="Street" />
</ContentPage>
Il prefisso dello spazio dei nomi local
può avere qualsiasi nome. I valori clr-namespace
e assembly
devono tuttavia corrispondere ai dettagli della mappa personalizzata. Dopo aver dichiarato lo spazio dei nomi, il prefisso viene usato per fare riferimento alla mappa personalizzata.
Nell'esempio di codice riportato di seguito viene illustrato come il controllo CustomMap
può essere usato da una pagina C#:
public class MapPageCS : ContentPage
{
public MapPageCS()
{
CustomMap customMap = new CustomMap
{
MapType = MapType.Street
};
// ...
Content = customMap;
}
}
L'istanza CustomMap
verrà usata per visualizzare la mappa nativa in ogni piattaforma. La proprietà MapType
imposta lo stile di visualizzazione di Map
con i valori possibili definiti nell'enumerazione MapType
.
La posizione della mappa e i segnaposto contenuti nella mappa vengono inizializzati come illustrato nell'esempio di codice seguente:
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)));
}
Questa inizializzazione aggiunge una puntina personalizzata e posiziona la visualizzazione della mappa con il metodo MoveToRegion
, che modifica la posizione e il livello di zoom della mappa mediante la creazione di MapSpan
da Position
e Distance
.
Un renderer personalizzato può essere ora aggiunto a ogni progetto di applicazione per personalizzare i controlli mappa nativa.
Creazione del renderer personalizzato in ogni piattaforma
Il processo di creazione della classe di renderer personalizzato è il seguente:
- Creare una sottoclasse della classe
MapRenderer
che esegue il rendering della mappa personalizzata. - Eseguire l'override del metodo
OnElementChanged
che esegue il rendering della mappa personalizzata e scrivere la logica per personalizzarlo. Questo metodo viene chiamato quando viene creata la mappa personalizzata corrispondente Xamarin.Forms . - Aggiungere un
ExportRenderer
attributo alla classe renderer personalizzata per specificare che verrà usata per eseguire il rendering della Xamarin.Forms mappa personalizzata. Questo attributo viene usato per registrare il renderer personalizzato con Xamarin.Forms.
Nota
La specifica di un renderer personalizzato nel progetto di ogni piattaforma è facoltativa. Se un renderer personalizzato non è registrato, verrà usato il renderer predefinito per la classe di base del controllo.
Il diagramma seguente illustra le responsabilità di ogni progetto nell'applicazione di esempio, insieme alle relazioni tra di essi:
Il rendering del controllo CustomMap
viene eseguito dalle classi di renderer specifiche della piattaforma che derivano dalla classe MapRenderer
per la piattaforma. Di conseguenza il rendering di ogni controllo CustomMap
viene eseguito con controlli specifici della piattaforma, come illustrato negli screenshot seguenti:
La MapRenderer
classe espone il OnElementChanged
metodo , che viene chiamato quando viene creata la mappa personalizzata per eseguire il Xamarin.Forms rendering del controllo nativo corrispondente. Questo metodo accetta un parametro ElementChangedEventArgs
che contiene le proprietà OldElement
e NewElement
. Queste proprietà rappresentano l'elemento Xamarin.Forms a cui è stato associato il renderer e l'elemento Xamarin.Forms a cui è associato il renderer. Nell'applicazione di esempio la proprietà OldElement
sarà null
e la proprietà NewElement
conterrà un riferimento all'istanza di CustomMap
.
La personalizzazione del controllo nativo deve essere eseguita in una versione sostituita del metodo OnElementChanged
in ogni classe di renderer specifica della piattaforma. Un riferimento tipizzato al controllo nativo usato nella piattaforma è accessibile attraverso la proprietà Control
. Inoltre, è possibile ottenere un riferimento al controllo di cui viene eseguito il Xamarin.Forms rendering tramite la Element
proprietà .
Prestare attenzione quando si sottoscrivono i gestori degli eventi nel metodo OnElementChanged
, come illustrato nell'esempio di codice seguente:
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
}
}
Il controllo nativo deve essere configurato e i gestori eventi sottoscritti solo quando il renderer personalizzato è collegato a un nuovo Xamarin.Forms elemento. Analogamente, per i gestori degli eventi sottoscritti l'iscrizione deve essere annullata solo in caso di modifica dell'elemento allegato al renderer. L'adozione di questo approccio consente di creare un renderer personalizzato che non subisce perdite di memoria.
Ogni classe renderer personalizzata è decorata con un ExportRenderer
attributo che registra il renderer con Xamarin.Forms. L'attributo accetta due parametri, ovvero il nome del tipo del controllo personalizzato di cui viene eseguito il Xamarin.Forms rendering e il nome del tipo del renderer personalizzato. Il prefisso assembly
dell'attributo specifica che l'attributo viene applicato all'intero assembly.
Le sezioni seguenti illustrano l'implementazione della classe renderer personalizzato specifica di ogni piattaforma.
Creazione del renderer personalizzato in iOS
Gli screenshot seguenti mostrano la mappa prima e dopo la personalizzazione:
In iOS il segnaposto viene chiamato annotazione e può essere un'immagine personalizzata o un segnaposto di vari colori definito dal sistema. Le annotazioni possono visualizzare un callout, che viene visualizzato in risposta alla selezione dell'annotazione da parte dell'utente. Il callout visualizza le proprietà Label
e Address
dell'istanza Pin
con le visualizzazioni accessorio sinistro e destro facoltative. Nello screenshot precedente la visualizzazione accessorio sinistro è costituita dall'immagine di una chiave inglese mentre la visualizzazione accessorio destro è costituita dal pulsante Informazioni.
L'esempio di codice seguente illustra il renderer personalizzato per la piattaforma 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;
}
}
// ...
}
}
Il OnElementChanged
metodo esegue la configurazione seguente dell'istanza MKMapView
, purché il renderer personalizzato sia collegato a un nuovo Xamarin.Forms elemento:
- La proprietà
GetViewForAnnotation
è impostata sul metodoGetViewForAnnotation
. Questo metodo viene chiamato quando la posizione dell'annotazione diventa visibile nella mappa e viene usato per personalizzare l'annotazione prima della visualizzazione. - I gestori per gli eventi
CalloutAccessoryControlTapped
,DidSelectAnnotationView
eDidDeselectAnnotationView
sono registrati. Questi eventi vengono attivati quando l'utente tocca l'accessorio destro nel callout e quando l'utente seleziona e deseleziona l'annotazione. Gli eventi sono annullati solo quando viene modificato l'elemento a cui il renderer è associato.
Visualizzazione dell'annotazione
Il metodo GetViewForAnnotation
viene chiamato quando la posizione dell'annotazione diventa visibile nella mappa e viene usato per personalizzare l'annotazione prima della visualizzazione. Un'annotazione è costituita da due parti:
MkAnnotation
: include il titolo, il sottotitolo e la posizione dell'annotazione.MkAnnotationView
: contiene l'immagine per rappresentare l'annotazione e, facoltativamente, un callout visualizzato quando l'utente tocca l'annotazione.
Il metodo GetViewForAnnotation
accetta IMKAnnotation
contenente i dati dell'annotazione e restituisce MKAnnotationView
per la visualizzazione nella mappa, come illustrato nell'esempio di codice seguente:
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;
}
Questo metodo assicura che l'annotazione venga visualizzata come immagine personalizzata anziché come segnaposto definito dal sistema e che quando viene toccata l'annotazione venga visualizzato un callout che contiene contenuti aggiuntivi a sinistra e a destra del titolo dell'annotazione e dell'indirizzo. Questo risultato viene ottenuto come segue:
- Il metodo
GetCustomPin
viene chiamato per restituire i dati del segnaposto personalizzato per l'annotazione. - Per risparmiare memoria, la visualizzazione dell'annotazione è in pool per essere riutilizzata con la chiamata a
DequeueReusableAnnotation
. - La classe
CustomMKAnnotationView
estende la classeMKAnnotationView
con le proprietàName
eUrl
che corrispondono alle stesse proprietà nell'istanzaCustomPin
. Viene creata una nuova istanza diCustomMKAnnotationView
, purché l'annotazione sianull
:- La proprietà
CustomMKAnnotationView.Image
viene impostata sull'immagine che rappresenterà l'annotazione nella mappa. - La proprietà
CustomMKAnnotationView.CalloutOffset
è impostata suCGPoint
che specifica che il callout verrà centrato sopra l'annotazione. - La proprietà
CustomMKAnnotationView.LeftCalloutAccessoryView
è impostata su un'immagine di una chiave inglese che verrà visualizzata a sinistra del titolo dell'annotazione e dell'indirizzo. - La proprietà
CustomMKAnnotationView.RightCalloutAccessoryView
è impostata su un pulsante Informazioni che verrà visualizzato a destra del titolo dell'annotazione e dell'indirizzo. - La proprietà
CustomMKAnnotationView.Name
è impostata sulla proprietàCustomPin.Name
restituita dal metodoGetCustomPin
. Ciò consente l'identificazione dell'annotazione in modo che sia possibile personalizzare ulteriormente il relativo callout, se necessario. - La proprietà
CustomMKAnnotationView.Url
è impostata sulla proprietàCustomPin.Url
restituita dal metodoGetCustomPin
. L'URL verrà aperto quando l'utente tocca il pulsante visualizzato nella visualizzazione accessorio callout destra.
- La proprietà
- La proprietà
MKAnnotationView.CanShowCallout
è impostata sutrue
in modo che venga visualizzato il callout quando viene toccata l'annotazione. - L'annotazione viene restituita per la visualizzazione nella mappa.
Selezione dell'annotazione
Quando l'utente tocca l'annotazione, viene attivato l'evento DidSelectAnnotationView
che a sua volta esegue il metodo 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);
}
}
Questo metodo estende il callout esistente (contenente le visualizzazioni accessorio destra e sinistra) aggiungendo un'istanza UIView
che contiene un'immagine del logo Xamarin, a condizione che la proprietà Name
dell'annotazione selezionata sia impostata su Xamarin
. Ciò rende possibili scenari in cui vengono visualizzati callout diversi per le diverse annotazioni. L'istanza UIView
verrà visualizzata al centro sopra il callout esistente.
Tocco sulla visualizzazione accessorio callout destra
Quando l'utente tocca il pulsante Informazioni nella visualizzazione accessorio callout destra, viene attivato l'evento CalloutAccessoryControlTapped
che a sua volta esegue il metodo 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));
}
}
Questo metodo apre un Web browser e passa all'indirizzo memorizzato nella proprietà CustomMKAnnotationView.Url
. Si noti che l'indirizzo è stato definito durante la creazione della raccolta CustomPin
nel progetto della libreria .NET Standard.
Deselezione dell'annotazione
Quando viene visualizzata l'annotazione e l'utente tocca la mappa, viene attivato l'evento DidDeselectAnnotationView
che a sua volta esegue il metodoOnDidDeselectAnnotationView
:
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
Questo metodo garantisce che quando il callout esistente non è selezionato venga interrotta anche la visualizzazione della parte estesa del callout (immagine del logo Xamarin) e vengano liberate le relative risorse.
Per altre informazioni sulla personalizzazione di un'istanza MKMapView
, vedere Mappe iOS.
Creazione del renderer personalizzato in Android
Gli screenshot seguenti mostrano la mappa prima e dopo la personalizzazione:
In Android il segnaposto viene chiamato indicatore e può essere un'immagine personalizzata o un indicatore di vari colori definito dal sistema. Gli indicatori possono visualizzare una finestra informazioni visualizzata quando l'utente tocca l'indicatore. La finestra informazioni visualizza le proprietà Label
e Address
dell'istanza Pin
e può essere personalizzata per includere altri contenuti. Tuttavia, è possibile visualizzare una sola finestra informazioni alla volta.
L'esempio di codice seguente illustra il renderer personalizzato per la piattaforma 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);
}
...
}
}
A condizione che il renderer personalizzato sia associato a un nuovo Xamarin.Forms elemento, il OnElementChanged
metodo recupera l'elenco di pin personalizzati dal controllo . Quando l'istanza GoogleMap
è disponibile, viene richiamato l'override OnMapReady
. Questo metodo registra un gestore per l'evento InfoWindowClick
che viene attivato quando viene fatto clic sulla finestra informazioni e viene annullato solo quando viene modificato l'elemento a cui è associato il renderer. L'override OnMapReady
chiama anche il metodo SetInfoWindowAdapter
per specificare che l'istanza della classe CustomMapRenderer
offrirà i metodi per personalizzare la finestra informazioni.
La classe CustomMapRenderer
implementa l'interfaccia GoogleMap.IInfoWindowAdapter
per personalizzare la finestra informazioni. Questa interfaccia consente di specificare l'implementazione dei metodi seguenti:
public Android.Views.View GetInfoWindow(Marker marker)
: questo metodo viene chiamato per restituire una finestra informazioni personalizzata per un indicatore. Se restituiscenull
, verrà usato il rendering della finestra predefinito. Se restituisceView
,View
verrà inserito all'interno della finestra informazioni.public Android.Views.View GetInfoContents(Marker marker)
: questo metodo viene chiamato per restituireView
con il contenuto della finestra informazioni e viene chiamato solo se il metodoGetInfoWindow
restituiscenull
. Se restituiscenull
, verrà usato il rendering predefinito del contenuto della finestra informazioni.
Nell'applicazione di esempio poiché viene personalizzato soltanto il contenuto della finestra informazioni, il metodo GetInfoWindow
restituisce null
per abilitare questa opzione.
Personalizzazione dell'indicatore
È possibile personalizzare l'icona che rappresenta l'indicatore chiamando il metodo MarkerOptions.SetIcon
. L'operazione può essere eseguita eseguendo l'override del metodo CreateMarker
che viene richiamato per ogni Pin
aggiunto alla mappa:
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;
}
Questo metodo crea una nuova istanza MarkerOption
per ogni istanza Pin
. Dopo aver impostato la posizione, l'etichetta e l'indirizzo dell'indicatore, l'icona viene impostata con il metodo SetIcon
. Questo metodo accetta un oggetto BitmapDescriptor
contenente i dati necessari per eseguire il rendering dell'icona, con la classe BitmapDescriptorFactory
che specifica i metodo dell'helper per semplificare la creazione di BitmapDescriptor
. Per altre informazioni sull'uso della classe BitmapDescriptorFactory
per personalizzare un indicatore, vedere Customizing a Marker (Personalizzazione di un indicatore).
Nota
Se necessario, il metodo GetMarkerForPin
può essere richiamato nel renderer della mappa per recuperare Marker
da Pin
.
Personalizzazione della finestra informazioni
Quando un utente tocca l'indicatore, viene eseguito il metodo GetInfoContents
, a condizione che il metodo GetInfoWindow
restituisca null
. L'esempio di codice seguente illustra il metodo 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;
}
Questo metodo restituisce View
con il contenuto della finestra informazioni. Questo risultato viene ottenuto come segue:
- Viene recuperata un'istanza
LayoutInflater
che viene usata per creare l'istanza di un file XML di layout nel valoreView
corrispondente. - Il metodo
GetCustomPin
viene chiamato per restituire i dati del segnaposto personalizzato per la finestra informazioni. - Il layout
XamarinMapInfoWindow
viene espanso se la proprietàCustomPin.Name
è uguale aXamarin
. In caso contrario, viene espanso il layoutMapInfoWindow
. Ciò rende possibili scenari in cui possono essere visualizzati layout diversi della finestra informazioni per i diversi indicatori. - Le risorse
InfoWindowTitle
eInfoWindowSubtitle
vengono recuperate dal layout espanso e le relative proprietàText
vengono impostate sui dati corrispondenti dell'istanzaMarker
, a condizione che le risorse non sianonull
. - L'istanza
View
viene restituita per la visualizzazione nella mappa.
Nota
Una finestra informazioni non è una View
attiva. Al contrario, Android convertirà la View
in una bitmap statica e la visualizzerà come immagine. Ciò significa che sebbene possa rispondere a un evento clic, la finestra informazioni non può rispondere agli eventi tocco, mentre i singoli controlli della finestra informazioni non possono rispondere ai relativi eventi clic.
Clic nella finestra informazioni
Quando l'utente fa clic nella finestra informazioni, viene attivato l'evento InfoWindowClick
che a sua volta esegue il metodo 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);
}
}
Questo metodo apre un Web browser e passa all'indirizzo memorizzato nella proprietà Url
dell'istanza CustomPin
recuperata per Marker
. Si noti che l'indirizzo è stato definito durante la creazione della raccolta CustomPin
nel progetto della libreria .NET Standard.
Per altre informazioni sulla personalizzazione di un'istanza MapView
, vedere API Maps.
Creazione del renderer personalizzato sulla piattaforma UWP (Universal Windows Platform)
Gli screenshot seguenti mostrano la mappa prima e dopo la personalizzazione:
Nella piattaforma UWP il segnaposto viene chiamato icona della mappa e può essere un'immagine personalizzata o immagine predefinita definita dal sistema. Un'icona della mappa può visualizzare UserControl
, visualizzato quando l'utente tocca l'icona della mappa. UserControl
può visualizzare qualsiasi contenuto, incluse le proprietà Label
e Address
dell'istanza Pin
.
L'esempio di codice seguente illustra il renderer personalizzato per la piattaforma 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);
}
}
}
...
}
}
Il OnElementChanged
metodo esegue le operazioni seguenti, purché il renderer personalizzato sia collegato a un nuovo Xamarin.Forms elemento:
- Cancella la raccolta
MapControl.Children
per rimuovere tutti gli elementi dell'interfaccia utente esistenti dalla mappa prima di registrare un gestore dell'evento per l'eventoMapElementClick
. Questo evento viene attivato quando l'utente tocca o fa clic suMapElement
inMapControl
e viene annullato solo quando viene modificato l'elemento a cui è associato il renderer. - Ogni segnaposto nella
customPins
raccolta viene visualizzato nella posizione geografica corretta sulla mappa come indicato di seguito:- Il percorso del segnaposto viene creato come istanza
Geopoint
. - Viene creata un'istanza
MapIcon
per rappresentare il segnaposto. - L'immagine usata per rappresentare
MapIcon
viene specificata impostando la proprietàMapIcon.Image
. Tuttavia, l'immagine dell'icona della mappa non viene visualizzata sempre poiché può essere nascosta da altri elementi nella mappa. Pertanto, la proprietàCollisionBehaviorDesired
dell'icona della mappa è impostata suMapElementCollisionBehavior.RemainVisible
per garantire che rimanga visibile. - La posizione di
MapIcon
viene specificata impostando la proprietàMapIcon.Location
. - La proprietà
MapIcon.NormalizedAnchorPoint
è impostata su una posizione approssimativa del puntatore nell'immagine. Se questa proprietà mantiene il valore predefinito (0,0), che rappresenta l'angolo superiore sinistro dell'immagine, le modifiche al livello di zoom della mappa potrebbero fare in modo che l'immagine punti a una posizione diversa. - L'istanza
MapIcon
viene aggiunta alla raccoltaMapControl.MapElements
. Il risultato è la visualizzazione dell'icona della mappa inMapControl
.
- Il percorso del segnaposto viene creato come istanza
Nota
Quando viene usata la stessa immagine per più icone della mappa, per ottenere le migliori prestazioni è necessario dichiarare l'istanza RandomAccessStreamReference
a livello di pagina o applicazione.
Visualizzazione di UserControl
Quando un utente tocca l'icona della mappa, viene eseguito il metodo OnMapElementClick
. L'esempio di codice seguente illustra il metodo:
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;
}
}
}
Questo metodo crea un'istanza UserControl
che visualizza informazioni sul segnaposto. Questo risultato viene ottenuto come segue:
- Viene recuperata l'istanza
MapIcon
. - Il metodo
GetCustomPin
viene chiamato per restituire i dati del segnaposto personalizzato da visualizzare. - L'istanza
XamarinMapOverlay
viene creata per visualizzare i dati del segnaposto personalizzato. Questa classe è un controllo utente. - La posizione geografica in cui visualizzare l'istanza
XamarinMapOverlay
inMapControl
viene creata come istanzaGeopoint
. - L'istanza
XamarinMapOverlay
viene aggiunta alla raccoltaMapControl.Children
. Questa raccolta contiene gli elementi dell'interfaccia utente XAML che verranno visualizzati nella mappa. - La posizione geografica dell'istanza
XamarinMapOverlay
nella mappa viene impostata tramite la chiamata al metodoSetLocation
. - La posizione relativa nell'istanza
XamarinMapOverlay
, che corrisponde alla posizione specificata, viene impostata tramite la chiamata al metodoSetNormalizedAnchorPoint
. In questo modo si garantisce che nonostante le modifiche apportate al livello di zoom della mappa, l'istanzaXamarinMapOverlay
venga sempre visualizzata nella posizione corretta.
In alternativa, se le informazioni sul segnaposto sono già visualizzate nella mappa, un tocco nella mappa rimuove l'istanza XamarinMapOverlay
dalla raccolta MapControl.Children
.
Tocco del pulsante Informazioni
Quando l'utente tocca il pulsante Informazioni nel controllo utente XamarinMapOverlay
, viene attivato l'evento Tapped
che a sua volta esegue il metodo OnInfoButtonTapped
:
private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}
Questo metodo apre un Web browser e passa all'indirizzo memorizzato nella proprietà Url
dell'istanza CustomPin
. Si noti che l'indirizzo è stato definito durante la creazione della raccolta CustomPin
nel progetto della libreria .NET Standard.
Per altre informazioni sulla personalizzazione di un'istanza MapControl
, vedere Panoramica su mappe e posizione in MSDN.