Dostosowywanie pinezki mapy

W tym artykule pokazano, jak utworzyć niestandardowy moduł renderowania dla kontrolki Mapa, który wyświetla natywną mapę z dostosowanym przypięciem i dostosowanym widokiem danych pinezki na każdej platformie.

Każdy Xamarin.Forms widok ma towarzyszący moduł renderujący dla każdej platformy, który tworzy wystąpienie kontrolki natywnej. Gdy element Map jest renderowany przez aplikację Xamarin.Forms w systemie iOS, MapRenderer klasę tworzy wystąpienie, co z kolei tworzy wystąpienie kontrolki natywnej MKMapView . Na platformie MapRenderer Android klasa tworzy wystąpienie natywnej MapView kontrolki. W platforma uniwersalna systemu Windows (UWP) MapRenderer klasa tworzy wystąpienie natywnej MapControlklasy . Aby uzyskać więcej informacji na temat klasy renderera i natywnych kontrolek mapowanych Xamarin.Forms na, zobacz Renderer Base Classes and Native Controls (Klasy bazowe modułu renderowania i kontrolki natywne).

Na poniższym diagramie przedstawiono relację między kontrolkami Map macierzystymi i odpowiadającymi im kontrolkami natywnymi, które je implementują:

Relacja między kontrolką mapy a implementacją kontrolek natywnych

Proces renderowania może służyć do implementowania dostosowań specyficznych dla platformy przez utworzenie niestandardowego modułu renderowania dla elementu na Map każdej platformie. Proces wykonywania tej czynności jest następujący:

  1. Utwórz mapę niestandardową Xamarin.Forms .
  2. Zużyj mapę niestandardową z pliku Xamarin.Forms.
  3. Utwórz niestandardowy moduł renderowania dla mapy na każdej platformie.

Każdy element zostanie omówiony z kolei, aby zaimplementować CustomMap moduł renderujący, który wyświetla mapę natywną z dostosowanym numerem PIN i dostosowanym widokiem danych pinezki na każdej platformie.

Uwaga

Xamarin.Forms.Maps należy zainicjować i skonfigurować przed użyciem. Aby uzyskać więcej informacji, zobacz Maps Control.

Tworzenie mapy niestandardowej

Niestandardową kontrolkę mapy można utworzyć przez podklasę Map klasy, jak pokazano w poniższym przykładzie kodu:

public class CustomMap : Map
{
    public List<CustomPin> CustomPins { get; set; }
}

Kontrolka CustomMap jest tworzona w projekcie biblioteki .NET Standard i definiuje interfejs API dla mapy niestandardowej. Mapa niestandardowa uwidacznia CustomPins właściwość reprezentującą kolekcję CustomPin obiektów, które będą renderowane przez natywną kontrolkę mapy na każdej platformie. Klasa jest pokazana CustomPin w poniższym przykładzie kodu:

public class CustomPin : Pin
{
    public string Name { get; set; }
    public string Url { get; set; }
}

Ta klasa definiuje CustomPin element jako dziedziczący właściwości Pin klasy oraz dodając Name i Url właściwości.

Korzystanie z mapy niestandardowej

Kontrolkę CustomMap można odwoływać w języku XAML w projekcie biblioteki .NET Standard, deklarując przestrzeń nazw dla swojej lokalizacji i używając prefiksu przestrzeni nazw w kontrolce mapy niestandardowej. Poniższy przykład kodu pokazuje, jak kontrolka CustomMap może być zużywana przez stronę XAML:

<ContentPage ...
			       xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
	<local:CustomMap x:Name="customMap"
                   MapType="Street" />
</ContentPage>

Prefiks local przestrzeni nazw może mieć nazwę dowolnych elementów. clr-namespace Jednak wartości i assembly muszą być zgodne ze szczegółami mapy niestandardowej. Po zadeklarowaniu przestrzeni nazw prefiks jest używany do odwołwania się do mapy niestandardowej.

Poniższy przykład kodu pokazuje, jak kontrolka CustomMap może być zużywana przez stronę języka C#:

public class MapPageCS : ContentPage
{
    public MapPageCS()
    {
        CustomMap customMap = new CustomMap
        {
            MapType = MapType.Street
        };
        // ...
        Content = customMap;
    }
}

Wystąpienie CustomMap będzie używane do wyświetlania mapy natywnej na każdej platformie. MapType Właściwość ustawia styl Mapwyświetlania obiektu , z możliwymi wartościami zdefiniowanymi w wyliczeniemMapType.

Lokalizacja mapy i zawarte w niej pinezki są inicjowane, jak pokazano w poniższym przykładzie kodu:

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)));
}

Ta inicjalizacja dodaje niestandardowy numer PIN i umieszcza widok mapy za MoveToRegion pomocą metody , która zmienia położenie i poziom powiększenia mapy, tworząc element MapSpan na podstawie elementu Position i Distance.

Niestandardowy moduł renderowania można teraz dodać do każdego projektu aplikacji w celu dostosowania natywnych kontrolek mapy.

Tworzenie niestandardowego modułu renderowania na każdej platformie

Proces tworzenia niestandardowej klasy renderera jest następujący:

  1. Utwórz podklasę MapRenderer klasy, która renderuje mapę niestandardową.
  2. Zastąpi metodę OnElementChanged , która renderuje mapę niestandardową i zapisuje logikę, aby ją dostosować. Ta metoda jest wywoływana podczas tworzenia odpowiedniej Xamarin.Forms mapy niestandardowej.
  3. ExportRenderer Dodaj atrybut do niestandardowej klasy renderera, aby określić, że będzie on używany do renderowania mapy niestandardowejXamarin.Forms. Ten atrybut służy do rejestrowania niestandardowego modułu renderowania za pomocą Xamarin.Formspolecenia .

Uwaga

Opcjonalne jest udostępnienie niestandardowego modułu renderowania w każdym projekcie platformy. Jeśli niestandardowy moduł renderowania nie jest zarejestrowany, zostanie użyty domyślny moduł renderowania dla klasy bazowej kontrolki.

Na poniższym diagramie przedstawiono obowiązki każdego projektu w przykładowej aplikacji wraz z relacjami między nimi:

Niestandardowe obowiązki projektu renderowania niestandardowego map

Kontrolka CustomMap jest renderowana przez klasy renderera specyficzne dla platformy, które pochodzą z MapRenderer klasy dla każdej platformy. Powoduje to renderowanie każdej CustomMap kontrolki z kontrolkami specyficznymi dla platformy, jak pokazano na poniższych zrzutach ekranu:

CustomMap na każdej platformie

Klasa MapRenderer uwidacznia metodę OnElementChanged , która jest wywoływana podczas Xamarin.Forms tworzenia mapy niestandardowej w celu renderowania odpowiedniej kontrolki natywnej. Ta metoda przyjmuje ElementChangedEventArgs parametr zawierający OldElement właściwości i NewElement . Te właściwości reprezentują Xamarin.Forms element dołączony do modułu renderowania, a Xamarin.Forms element, do którego jest dołączony moduł renderujący. W przykładowej aplikacji OldElement właściwość będzie i nullNewElement właściwość będzie zawierać odwołanie do CustomMap wystąpienia.

Zastąpiona wersja metody , w każdej klasie modułu OnElementChanged renderowania specyficznego dla platformy, to miejsce do wykonania dostosowania natywnej kontrolki. Za pośrednictwem właściwości można uzyskać dostęp do wpisanego odwołania do natywnej kontrolki używanej Control na platformie. Ponadto można uzyskać odwołanie do Xamarin.Forms renderowanej kontrolki Element za pośrednictwem właściwości .

Podczas subskrybowania programów obsługi zdarzeń w metodzie OnElementChanged należy zachować ostrożność, jak pokazano w poniższym przykładzie kodu:

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
  }
}

Kontrolka natywna powinna być skonfigurowana i programy obsługi zdarzeń subskrybowane tylko wtedy, gdy niestandardowy moduł renderujący jest dołączony do nowego Xamarin.Forms elementu. Podobnie wszystkie programy obsługi zdarzeń, które zostały zasubskrybowane, powinny zostać anulowane tylko wtedy, gdy element renderujący jest dołączony do zmian. Wdrożenie tego podejścia pomoże utworzyć niestandardowy moduł renderujący, który nie cierpi na przecieki pamięci.

Każda niestandardowa klasa modułu renderowania jest ozdobiona atrybutem ExportRenderer , który rejestruje program renderujący za pomocą Xamarin.Formspolecenia . Atrybut przyjmuje dwa parametry — nazwę Xamarin.Forms typu renderowanej kontrolki niestandardowej i nazwę typu niestandardowego modułu renderowania. Prefiks assembly atrybutu określa, że atrybut ma zastosowanie do całego zestawu.

W poniższych sekcjach omówiono implementację poszczególnych niestandardowych klas renderer specyficznych dla platformy.

Tworzenie niestandardowego modułu renderowania w systemie iOS

Na poniższych zrzutach ekranu przedstawiono mapę przed dostosowaniem i po nim:

Zrzuty ekranu przedstawiają urządzenie przenośne ze zwykłym przypięciem i przypięciem z adnotacjami.

W systemie iOS numer PIN jest nazywany adnotacją i może być obrazem niestandardowym lub numerem PIN zdefiniowanym przez system różnych kolorów. Adnotacje mogą opcjonalnie wyświetlać objaśnienie, które jest wyświetlane w odpowiedzi na użytkownika wybierającego adnotację. Objaśnienie wyświetla Label właściwości Pin i Address wystąpienia z opcjonalnymi widokami dostępu po lewej i prawej stronie. Na powyższym zrzucie ekranu lewy widok akcesoriów jest obrazem małpy, z odpowiednim widokiem akcesorium jest przycisk Informacje .

Poniższy przykład kodu przedstawia niestandardowy moduł renderowania dla platformy 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;
            }
        }
        // ...
    }
}

Metoda OnElementChanged wykonuje następującą konfigurację MKMapView wystąpienia, pod warunkiem, że niestandardowy moduł renderujący jest dołączony do nowego Xamarin.Forms elementu:

Wyświetlanie adnotacji

Metoda GetViewForAnnotation jest wywoływana, gdy lokalizacja adnotacji staje się widoczna na mapie i służy do dostosowywania adnotacji przed wyświetleniem. Adnotacja ma dwie części:

  • MkAnnotation — zawiera tytuł, podtytuł i lokalizację adnotacji.
  • MkAnnotationView — zawiera obraz reprezentujący adnotację i opcjonalnie objaśnienie wyświetlane po naciśnięciu adnotacji.

Metoda GetViewForAnnotation akceptuje element IMKAnnotation zawierający dane adnotacji i zwraca element MKAnnotationView do wyświetlania na mapie i jest wyświetlany w poniższym przykładzie kodu:

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;
}

Ta metoda gwarantuje, że adnotacja będzie wyświetlana jako obraz niestandardowy, a nie jako numer PIN zdefiniowany przez system, a gdy adnotacja zostanie naciśnięta, zostanie wyświetlona objaśnienie zawierające dodatkową zawartość po lewej i prawej stronie tytułu adnotacji i adresu. Jest to realizowane w następujący sposób:

  1. Metoda jest wywoływana GetCustomPin w celu zwrócenia niestandardowych danych pin dla adnotacji.
  2. Aby zaoszczędzić pamięć, widok adnotacji jest w puli do ponownego użycia za pomocą wywołania metody DequeueReusableAnnotation.
  3. Klasa CustomMKAnnotationView rozszerza klasę o MKAnnotationViewName właściwości i Url odpowiadające identycznym właściwościom w wystąpieniu CustomPin . Zostanie utworzone nowe wystąpienie CustomMKAnnotationView elementu , pod warunkiem że adnotacja to null:
    • Właściwość jest ustawiona CustomMKAnnotationView.Image na obraz, który będzie reprezentować adnotację na mapie.
    • Właściwość CustomMKAnnotationView.CalloutOffset jest ustawiona na wartość CGPoint , która określa, że objaśnienie będzie wyśrodkowane powyżej adnotacji.
    • Właściwość CustomMKAnnotationView.LeftCalloutAccessoryView jest ustawiona na obraz małpy, która będzie widoczna po lewej stronie tytułu i adresu adnotacji.
    • Właściwość CustomMKAnnotationView.RightCalloutAccessoryView jest ustawiona na przycisk Informacje , który będzie wyświetlany po prawej stronie tytułu adnotacji i adresu.
    • Właściwość CustomMKAnnotationView.Name jest ustawiona CustomPin.Name na właściwość zwracaną przez metodę GetCustomPin . Dzięki temu adnotacja może zostać zidentyfikowana, aby można było ją dodatkowo dostosować, jeśli jest to konieczne.
    • Właściwość CustomMKAnnotationView.Url jest ustawiona CustomPin.Url na właściwość zwracaną przez metodę GetCustomPin . Adres URL zostanie wyświetlony, gdy użytkownik naciągnie przycisk wyświetlany w prawym widoku dostępu objaśnień.
  4. Właściwość MKAnnotationView.CanShowCallout jest ustawiona na true wartość , aby objaśnienie było wyświetlane po naciśnięciu adnotacji.
  5. Adnotacja jest zwracana do wyświetlania na mapie.

Wybieranie adnotacji

Gdy użytkownik naciśnie adnotację, DidSelectAnnotationView zdarzenie zostanie uruchomione OnDidSelectAnnotationView , co z kolei spowoduje wykonanie metody :

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);
    }
}

Ta metoda rozszerza istniejące objaśnienie (zawierające widoki akcesoriów po lewej i prawej stronie), dodając UIView do niego wystąpienie zawierające obraz logo platformy Xamarin, pod warunkiem że wybrana adnotacja ma jej Name właściwość ustawioną na Xamarinwartość . Pozwala to na scenariusze, w których różne objaśnienia mogą być wyświetlane dla różnych adnotacji. Wystąpienie UIView zostanie wyświetlone wyśrodkowane powyżej istniejącego objaśnienie.

Naciśnięcie odpowiedniego widoku akcesoriów objaśniania

Gdy użytkownik naciągnie przycisk Informacje w prawym widoku CalloutAccessoryControlTapped wywołania, zdarzenie jest uruchamiane, co z kolei wykonuje metodę 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));
    }
}

Ta metoda otwiera przeglądarkę internetową i przechodzi do adresu przechowywanego CustomMKAnnotationView.Url we właściwości . Należy pamiętać, że adres został zdefiniowany podczas tworzenia CustomPin kolekcji w projekcie biblioteki .NET Standard.

Usuwanie zaznaczenia adnotacji

Gdy adnotacja jest wyświetlana i użytkownik naciśnie na mapie, DidDeselectAnnotationView zdarzenie jest uruchamiane, co z kolei wykonuje metodę OnDidDeselectAnnotationView :

void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
    if (!e.View.Selected)
    {
        customPinView.RemoveFromSuperview();
        customPinView.Dispose();
        customPinView = null;
    }
}

Ta metoda gwarantuje, że po wybraniu istniejącego objaśnienia rozszerzona część objaśnienia (obraz logo platformy Xamarin) również przestanie być wyświetlana, a jej zasoby zostaną zwolnione.

Aby uzyskać więcej informacji na temat dostosowywania MKMapView wystąpienia, zobacz iOS Mapy.

Tworzenie niestandardowego modułu renderowania w systemie Android

Na poniższych zrzutach ekranu przedstawiono mapę przed dostosowaniem i po nim:

Zrzuty ekranu przedstawiają urządzenie przenośne ze zwykłym znacznikiem i dostosowanym znacznikiem.

W systemie Android numer PIN jest nazywany znacznikiem i może być obrazem niestandardowym lub znacznikiem zdefiniowanym przez system różnych kolorów. Znaczniki mogą wyświetlać okno informacji, które jest wyświetlane w odpowiedzi na naciśnięcie znacznika przez użytkownika. W oknie informacji są wyświetlane Label właściwości Pin i Address wystąpienia, które można dostosować w celu uwzględnienia innej zawartości. Można jednak wyświetlić tylko jedno okno informacji jednocześnie.

W poniższym przykładzie kodu pokazano niestandardowy moduł renderowania dla platformy 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);
        }
        ...
    }
}

Jeśli niestandardowy moduł renderujący jest dołączony do nowego Xamarin.Forms elementu, OnElementChanged metoda pobiera listę niestandardowych numerów PIN z kontrolki. Po udostępnieniu GoogleMapOnMapReady wystąpienia zostanie wywołane przesłonięcia. Ta metoda rejestruje program obsługi zdarzeń dla InfoWindowClick zdarzenia, który jest uruchamiany po kliknięciu okna informacji i jest anulowany tylko wtedy, gdy element renderator jest dołączony do zmian. Przesłonięcia OnMapReady wywołuje również metodę SetInfoWindowAdapter , aby określić, że CustomMapRenderer wystąpienie klasy zapewni metody dostosowywania okna informacji.

Klasa CustomMapRenderer implementuje interfejs w GoogleMap.IInfoWindowAdapter celu dostosowania okna informacji. Ten interfejs określa, że należy zaimplementować następujące metody:

  • public Android.Views.View GetInfoWindow(Marker marker) — Ta metoda jest wywoływana w celu zwrócenia niestandardowego okna informacji dla znacznika. Jeśli zwróci nullwartość , zostanie użyte domyślne renderowanie okna. Jeśli zwróci wartość View, View zostanie ona umieszczona wewnątrz okna informacji.
  • public Android.Views.View GetInfoContents(Marker marker) — Ta metoda jest wywoływana w celu zwrócenia View zawartości okna informacji i będzie wywoływana tylko wtedy, gdy GetInfoWindow metoda zwróci nullwartość . Jeśli zwróci nullwartość , zostanie użyte domyślne renderowanie zawartości okna informacji.

W przykładowej aplikacji dostosowywuje się tylko zawartość okna informacji, dlatego GetInfoWindow metoda zwraca null tę funkcję.

Dostosowywanie znacznika

Ikona używana do reprezentowania znacznika można dostosować, wywołując metodę MarkerOptions.SetIcon . Można to zrobić przez zastąpienie CreateMarker metody, która jest wywoływana dla każdego Pin dodanego do mapy:

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;
}

Ta metoda tworzy nowe MarkerOption wystąpienie dla każdego Pin wystąpienia. Po ustawieniu pozycji, etykiety i adresu znacznika jego ikona jest ustawiona za pomocą SetIcon metody . Ta metoda przyjmuje BitmapDescriptor obiekt zawierający dane niezbędne do renderowania ikony, a BitmapDescriptorFactory klasa udostępnia metody pomocnicze, aby uprościć tworzenie BitmapDescriptorobiektu . Aby uzyskać więcej informacji na temat dostosowywania znacznika BitmapDescriptorFactory przy użyciu klasy, zobacz Dostosowywanie znacznika.

Uwaga

Jeśli jest to wymagane, można wywołać metodę w narzędziu GetMarkerForPin renderowanym mapy, aby pobrać element Marker z elementu Pin.

Dostosowywanie okna informacji

Gdy użytkownik naciągnie znacznik, GetInfoContents metoda jest wykonywana, pod warunkiem, że GetInfoWindow metoda zwraca nullwartość . Poniższy przykład kodu przedstawia metodę 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;
}

Ta metoda zwraca View zawartość okna informacji. Jest to realizowane w następujący sposób:

  • Pobrano LayoutInflater wystąpienie. Służy do tworzenia wystąpienia pliku XML układu w odpowiadającym mu Viewpliku .
  • Metoda jest wywoływana GetCustomPin w celu zwrócenia niestandardowych danych pin dla okna informacji.
  • Układ XamarinMapInfoWindow jest zawyżony, jeśli CustomPin.Name właściwość jest równa Xamarin. MapInfoWindow W przeciwnym razie układ jest zawyżony. Pozwala to na scenariusze, w których różne układy okien informacyjnych mogą być wyświetlane dla różnych znaczników.
  • Zasoby InfoWindowTitle i InfoWindowSubtitle są pobierane z zawyżonego układu, a ich Text właściwości są ustawione na odpowiednie dane z Marker wystąpienia, pod warunkiem, że zasoby nie nullsą .
  • Wystąpienie View jest zwracane do wyświetlania na mapie.

Uwaga

Okno informacji nie jest aktywne View. Zamiast tego system Android przekonwertuje View element na statyczną mapę bitową i wyświetli go jako obraz. Oznacza to, że chociaż okno informacji może odpowiedzieć na zdarzenie kliknięcia, nie może reagować na żadne zdarzenia lub gesty dotykowe, a poszczególne kontrolki w oknie informacji nie mogą reagować na własne zdarzenia kliknięcia.

Kliknięcie okna informacji

Gdy użytkownik kliknie okno informacji, InfoWindowClick zdarzenie zostanie wyzwolony, co z kolei spowoduje wykonanie OnInfoWindowClick metody :

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);
    }
}

Ta metoda otwiera przeglądarkę internetową i przechodzi do adresu przechowywanego we Url właściwości pobranego CustomPin wystąpienia dla elementu Marker. Należy pamiętać, że adres został zdefiniowany podczas tworzenia CustomPin kolekcji w projekcie biblioteki .NET Standard.

Aby uzyskać więcej informacji na temat dostosowywania MapView wystąpienia, zobacz interfejs API Mapy.

Tworzenie niestandardowego modułu renderowania na platforma uniwersalna systemu Windows

Na poniższych zrzutach ekranu przedstawiono mapę przed dostosowaniem i po nim:

Zrzuty ekranu przedstawiają urządzenie przenośne ze zwykłą ikoną mapy i dostosowaną ikoną mapy.

Na platformie UWP pinezka jest nazywana ikoną mapy i może być obrazem niestandardowym lub obrazem domyślnym zdefiniowanym przez system. Ikona mapy może wyświetlić ikonę UserControl, która jest wyświetlana w odpowiedzi na naciśnięcie przez użytkownika ikony mapy. Element UserControl może wyświetlać dowolną zawartość, w tym Label właściwości Pin i Address wystąpienia.

W poniższym przykładzie kodu pokazano niestandardowy moduł renderowania platformy 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);                    
                }
            }
        }
        ...
    }
}

Metoda OnElementChanged wykonuje następujące operacje, pod warunkiem, że niestandardowy moduł renderujący jest dołączony do nowego Xamarin.Forms elementu:

  • Spowoduje to wyczyszczenie MapControl.Children kolekcji w celu usunięcia wszystkich istniejących elementów interfejsu użytkownika z mapy przed zarejestrowaniem programu obsługi zdarzeń dla MapElementClick zdarzenia. To zdarzenie jest uruchamiane, gdy użytkownik naciągnie lub kliknie element MapElement na MapControlobiekcie , i zostanie anulowane tylko wtedy, gdy element renderator jest dołączony do zmian.
  • Każde przypięcie customPins w kolekcji jest wyświetlane w odpowiedniej lokalizacji geograficznej na mapie w następujący sposób:
    • Lokalizacja pinezki jest tworzona Geopoint jako wystąpienie.
    • Zostanie MapIcon utworzone wystąpienie reprezentujące numer PIN.
    • Obraz używany do reprezentowania MapIcon elementu jest określony przez ustawienie MapIcon.Image właściwości . Jednak obraz ikony mapy nie zawsze jest wyświetlany, ponieważ może być zasłonięty przez inne elementy na mapie. W związku z tym właściwość ikony CollisionBehaviorDesired mapy jest ustawiona na MapElementCollisionBehavior.RemainVisible, aby upewnić się, że pozostaje widoczna.
    • Lokalizacja MapIcon obiektu jest określana przez ustawienie MapIcon.Location właściwości .
    • Właściwość MapIcon.NormalizedAnchorPoint jest ustawiona na przybliżoną lokalizację wskaźnika na obrazie. Jeśli ta właściwość zachowuje domyślną wartość (0,0), która reprezentuje lewy górny róg obrazu, zmiany na poziomie powiększenia mapy mogą spowodować wyświetlenie obrazu wskazującego inną lokalizację.
    • Wystąpienie MapIcon jest dodawane do kolekcji MapControl.MapElements . Spowoduje to wyświetlenie ikony mapy na MapControl.

Uwaga

W przypadku używania tego samego obrazu dla wielu ikon RandomAccessStreamReference mapy wystąpienie powinno zostać zadeklarowane na poziomie strony lub aplikacji w celu uzyskania najlepszej wydajności.

Wyświetlanie kontrolek użytkownika

Gdy użytkownik naciągnie ikonę mapy, OnMapElementClick zostanie wykonana metoda. Poniższy przykład kodu przedstawia tę metodę:

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;
        }
    }
}

Ta metoda tworzy UserControl wystąpienie, które wyświetla informacje o numerze PIN. Jest to realizowane w następujący sposób:

  • Wystąpienie MapIcon jest pobierane.
  • Metoda jest wywoływana GetCustomPin w celu zwrócenia niestandardowych danych pin, które zostaną wyświetlone.
  • Zostanie XamarinMapOverlay utworzone wystąpienie w celu wyświetlenia niestandardowych danych pinezki. Ta klasa jest kontrolką użytkownika.
  • Lokalizacja geograficzna, w której ma być wyświetlane XamarinMapOverlay wystąpienie, MapControl jest tworzone jako Geopoint wystąpienie.
  • Wystąpienie XamarinMapOverlay jest dodawane do kolekcji MapControl.Children . Ta kolekcja zawiera elementy interfejsu użytkownika XAML, które będą wyświetlane na mapie.
  • Lokalizacja XamarinMapOverlay geograficzna wystąpienia na mapie jest ustawiana przez wywołanie SetLocation metody .
  • Lokalizacja względna w wystąpieniu XamarinMapOverlay , która odpowiada określonej lokalizacji, jest ustawiana przez wywołanie SetNormalizedAnchorPoint metody . Gwarantuje to, że zmiany na poziomie powiększenia mapy powodują XamarinMapOverlay , że wystąpienie zawsze jest wyświetlane w odpowiedniej lokalizacji.

Alternatywnie, jeśli informacje o numerze PIN są już wyświetlane na mapie, naciśnięcie mapy spowoduje usunięcie XamarinMapOverlay wystąpienia z MapControl.Children kolekcji.

Naciśnięcie przycisku Informacje

Gdy użytkownik naciągnie przycisk Informacje w kontrolce XamarinMapOverlay użytkownika, Tapped zdarzenie zostanie wyzwolony, co z kolei spowoduje wykonanie OnInfoButtonTapped metody:

private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)
{
    await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}

Ta metoda otwiera przeglądarkę internetową i przechodzi do adresu przechowywanego Url we właściwości CustomPin wystąpienia. Należy pamiętać, że adres został zdefiniowany podczas tworzenia CustomPin kolekcji w projekcie biblioteki .NET Standard.

Aby uzyskać więcej informacji na temat dostosowywania MapControl wystąpienia, zobacz Mapy i Location Overview on MSDN (Omówienie lokalizacji i Mapy w witrynie MSDN).