Настройка закрепления карты
В этой статье содержатся сведения о создании настраиваемого отрисовщика для элемента управления Map, который отображает собственную карту с настраиваемым закреплением и настраиваемым представлением закрепленных данных на каждой платформе.
Каждое представление Xamarin.Forms имеет сопутствующий отрисовщик для каждой платформы, который создает экземпляр собственного элемента управления. Когда Map
отображается в приложении Xamarin.Forms, на платформе iOS создается класс MapRenderer
, который, в свою очередь, создает собственный элемент управления MKMapView
. На платформе Android класс MapRenderer
создает собственный элемент управления MapView
. На универсальной платформе Windows (UWP) класс MapRenderer
создает собственный элемент управления MapControl
. Дополнительные сведения об отрисовщике и классах собственных элементов управления, с которыми сопоставляются элементы управления Xamarin.Forms, см. в статье Базовые классы и собственные элементы управления отрисовщика.
На следующей схеме показана связь между классом Map
и соответствующими собственными элементами управления, которые его реализуют:
Процесс отрисовки можно использовать для реализации настроек конкретных платформ путем создания настраиваемого отрисовщика для Map
на каждой платформе. Этот процесс выглядит следующим образом:
- создание настраиваемой карты Xamarin.Forms;
- использование настраиваемой карты в Xamarin.Forms;
- создание настраиваемого отрисовщика для карты на каждой платформе.
Далее мы рассмотрим каждый элемент отдельно в целях реализации отрисовщика CustomMap
, который отображает собственную карту с настраиваемым закреплением, а также настраиваемое представление закрепленных данных на каждой платформе.
Примечание.
Перед использованием необходимо инициализировать и настроить Xamarin.Forms.Maps
. Дополнительные сведения см. в разделе Maps Control
.
Создание пользовательской карты
Настраиваемый элемент управления картой можно создать путем создания подклассов класса Map
, как показано в следующем примере кода:
public class CustomMap : Map
{
public List<CustomPin> CustomPins { get; set; }
}
Элемент управления CustomMap
создается в проекте библиотеки .NET Standard и определяет API для настраиваемой карты. Настраиваемая карта предоставляет свойство CustomPins
, представляющее коллекцию объектов CustomPin
, которые будут отрисовываться собственным элементом управления картой на каждой платформе. Класс CustomPin
показан в следующем примере кода:
public class CustomPin : Pin
{
public string Name { get; set; }
public string Url { get; set; }
}
Этот класс определяет CustomPin
путем наследования свойств класса Pin
с добавлением свойств Name
и Url
.
Использование пользовательской карты
На элемент управления CustomMap
можно ссылаться в XAML в проекте библиотеки .NET Standard, объявив пространство имен для его расположения и используя префикс пространства имен в настраиваемом элементе управления картой. В следующем примере кода показано, как элемент управления CustomMap
может использоваться страницей XAML.
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<local:CustomMap x:Name="customMap"
MapType="Street" />
</ContentPage>
Префикс пространства имен local
может иметь любое имя. Тем не менее значения clr-namespace
и assembly
должны соответствовать данным настраиваемой карты. После объявления пространства имен префикс используется для ссылки на настраиваемую карту.
В следующем примере кода показано, как элемент управления CustomMap
может использоваться страницей C#:
public class MapPageCS : ContentPage
{
public MapPageCS()
{
CustomMap customMap = new CustomMap
{
MapType = MapType.Street
};
// ...
Content = customMap;
}
}
Экземпляр CustomMap
будет использоваться для отображения собственной карты на каждой платформе. Его свойство MapType
задает стиль отображения для Map
. При этом возможные значения определяются в перечислении MapType
.
Местоположение на карте и содержащиеся на ней закрепления инициализируются способом, показанным в следующем примере кода:
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)));
}
При инициализации добавляются настраиваемые закрепление и позиции, а затем представление карты позиционируется с помощью метода MoveToRegion
. При этом положение и масштаб карты изменяются путем создания объекта MapSpan
на основе Position
и Distance
.
Теперь в каждый проект приложения можно добавить настраиваемый отрисовщик для настройки собственных элементов управления картой.
Создание пользовательского отрисовщика на каждой платформе
Процесс создания класса пользовательского отрисовщика выглядит следующим образом:
- Создайте подкласс класса
MapRenderer
, который отрисовывает настраиваемую карту. - Переопределите метод
OnElementChanged
, который отрисовывает настраиваемую карту, и напишите логику для ее настройки. Этот метод вызывается, когда создается соответствующая настраиваемая карта Xamarin.Forms. - Добавьте атрибут
ExportRenderer
в класс настраиваемого отрисовщика, чтобы указать, что он будет использоваться для отрисовки настраиваемой карты Xamarin.Forms. Этот атрибут используется для регистрации пользовательского отрисовщика в Xamarin.Forms.
Примечание.
Предоставлять пользовательский отрисовщик в проекте для каждой платформы не обязательно. Если пользовательский отрисовщик не зарегистрирован, будет использоваться отрисовщик по умолчанию для базового класса элемента управления.
На следующей схеме показаны обязанности каждого проекта в примере приложения, а также связи между ними:
Настраиваемый элемент управления CustomMap
отрисовывается с помощью зависящих от платформы классов отрисовщика, которые являются производными от класса MapRenderer
для каждой платформы. Это приводит к тому, что каждый элемент управления CustomMap
отрисовывается с помощью зависящих от платформы элементов управления, как показано на следующих снимках экрана:
Класс MapRenderer
предоставляет метод OnElementChanged
, который вызывается при создании настраиваемой карты Xamarin.Forms для отрисовки соответствующего собственного элемента управления. Этот метод принимает параметр ElementChangedEventArgs
, содержащий свойства OldElement
и NewElement
. Эти свойства представляют элемент Xamarin.Forms, к которому был присоединен отрисовщик, и элемент Xamarin.Forms, к которому присоединен отрисовщик, соответственно. В примере приложения свойство OldElement
будет иметь значение null
, а свойство NewElement
будет содержать ссылку на экземпляр CustomMap
.
Настройка собственного элемента управления выполняется в переопределенной версии метода OnElementChanged
в классе отрисовщика для каждой платформы. Типизированную ссылку на собственный элемент управления, используемый на платформе, можно получить через свойство Control
. Кроме того, ссылку на отрисовываемый элемент управления Xamarin.Forms можно получить с помощью свойства Element
.
Будьте осторожны при подписке на обработчики событий в методе OnElementChanged
, как показано в следующем примере кода:
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
}
}
Собственный элемент управления следует настраивать и подписывать на обработчики событий только при присоединении настраиваемого отрисовщика к новому элементу Xamarin.Forms. Аналогично, от всех обработчиков событий, на которые были созданы подписки, следует отписываться только при изменении элемента, к которому присоединен отрисовщик. Реализовав этот подход, вы сможете создать пользовательский отрисовщик, на который не влияют утечки памяти.
Каждый класс пользовательского отрисовщика дополняется атрибутом ExportRenderer
, который регистрирует отрисовщик в Xamarin.Forms. Атрибут принимает два параметра — имя типа отрисовываемого пользовательского элемента управления Xamarin.Forms и имя типа пользовательского отрисовщика. Префикс assembly
в атрибуте указывает, что атрибут применяется ко всей сборке.
В следующих разделах рассматривается реализация класса пользовательского отрисовщика для каждой платформы.
Создание пользовательского отрисовщика в iOS
На следующих снимках экрана показана карта до и после настройки:
В iOS закрепление называется заметкой и может представлять собой настраиваемое изображение или задаваемое системой закрепление различных цветов. В заметках также может отображаться выноска, которая выводится на экран при выборе заметки пользователем. В выноске отображаются свойства Label
и Address
экземпляра Pin
с необязательными левым и правым вспомогательными представлениями. На представленном выше снимке экрана левое вспомогательное представление содержит изображение обезьяны, а в правом находится кнопка Сведения.
В следующем примере кода показан пользовательский отрисовщик для платформы 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;
}
}
// ...
}
}
Метод OnElementChanged
выполняет следующие настройки для экземпляра MKMapView
, если настраиваемый отрисовщик присоединен к новому элементу Xamarin.Forms:
- Свойство
GetViewForAnnotation
содержит методGetViewForAnnotation
. Этот метод вызывается в том случае, когда на карте отображается местоположение заметки и используется для настройки заметки перед ее выводом на экран. - Обработчики событий для событий
CalloutAccessoryControlTapped
,DidSelectAnnotationView
иDidDeselectAnnotationView
регистрируются. Эти события вызываются в том случае, если пользователь касается правого вспомогательного представления в выноске, а также когда пользователь выбирает заметку и отменяет ее выбор соответственно. Подписка на эти события отменяется, если изменяется элемент Xamarin.Forms, к которому присоединен отрисовщик.
Отображение заметки
Метод GetViewForAnnotation
вызывается, когда на карте отображается местоположение заметки, и используется для настройки заметки перед ее выводом на экран. Заметка состоит из двух частей:
MkAnnotation
— содержит заголовок, подзаголовок и местоположение заметки.MkAnnotationView
— содержит изображение, которое представляет заметку, а также может содержать выноску, которая отображается в том случае, если пользователь касается заметки.
Метод GetViewForAnnotation
принимает объект IMKAnnotation
, который содержит данные заметки, и возвращает объект MKAnnotationView
, отображаемый на карте, как показано в следующем примере кода:
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;
}
Этот метод позволяет вывести в качестве заметки настраиваемое изображение вместо определяемого системой закрепления, а также отобразить выноску с дополнительным содержимым слева и справа от заголовка и адреса заметки в том случае, если пользователь коснется заметки. Это реализуется следующим образом:
- Вызывается метод
GetCustomPin
, который возвращает данные настраиваемого закрепления для заметки. - В целях экономии памяти представление заметки помещается в пул для повторного использования путем вызова
DequeueReusableAnnotation
. - Класс
CustomMKAnnotationView
расширяет классMKAnnotationView
с использованием свойствName
иUrl
, которые соответствуют аналогичным свойствам экземпляраCustomPin
. Создается новый экземплярCustomMKAnnotationView
, при условии, что заметка :null
- Свойство
CustomMKAnnotationView.Image
содержит изображение, которое будет представлять заметку на карте. - Свойство
CustomMKAnnotationView.CalloutOffset
содержит объектCGPoint
, который указывает, что выноска будет выровнена по центру относительно заметки. - Свойство
CustomMKAnnotationView.LeftCalloutAccessoryView
содержит изображение, которое будет отображаться слева от заголовка и адреса заметки. - Свойство
CustomMKAnnotationView.RightCalloutAccessoryView
содержит кнопку Сведения, которая будет отображаться справа от заголовка и адреса заметки. - Свойство
CustomMKAnnotationView.Name
содержит свойствоCustomPin.Name
, возвращаемое методомGetCustomPin
. Это позволяет идентифицировать заметку для дальнейшей настройки выноски, если это требуется. - Свойство
CustomMKAnnotationView.Url
содержит свойствоCustomPin.Url
, возвращаемое методомGetCustomPin
. URL-адрес, по которому будет выполнен переход в том случае, если пользователь коснется кнопки, отображаемой в правом вспомогательном представлении выноски.
- Свойство
- Свойству
MKAnnotationView.CanShowCallout
присваивается значениеtrue
, которое задает отображение выноски в том случае, если пользователь коснется заметки. - Возвращается заметка, которая будет отображаться на карте.
Выбор заметки
Когда пользователь касается заметки, вызывается событие DidSelectAnnotationView
, которое, в свою очередь, выполняет метод 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);
}
}
Этот метод расширяет существующую выноску, которая содержит левое и правое вспомогательные представления, добавляя к ней экземпляр UIView
с изображением логотипа Xamarin при условии, что свойству Name
заметки присвоено значение Xamarin
. Это позволяет реализовать сценарии, в которых для разных заметок могут отображаться разные выноски. Экземпляр UIView
будет отображаться по центру над существующей выноской.
Касание правого вспомогательного представления выноски
Когда пользователь касается кнопки Сведения в правом вспомогательном представлении выноски, вызывается событие CalloutAccessoryControlTapped
, которое, в свою очередь, выполняет метод 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));
}
}
Этот метод открывает веб-браузер и выполняет переход по адресу, который хранится в свойстве CustomMKAnnotationView.Url
. Обратите внимание, что этот адрес определяется при создании коллекции CustomPin
в проекте библиотеки .NET Standard.
Отмена выбора заметки
Если заметка отображается на экране и пользователь касается карты, вызывается событие DidDeselectAnnotationView
, которое, в свою очередь, выполняет метод OnDidDeselectAnnotationView
:
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
Этот метод гарантирует, что в тех случаях, когда существующая выноска не выбрана, ее дополнительные части (изображение логотипа Xamarin) также скрываются, а соответствующие ресурсы высвобождаются.
Дополнительные сведения о настройке экземпляра MKMapView
см. в статье iOS Maps.
Создание пользовательского отрисовщика в Android
На следующих снимках экрана показана карта до и после настройки:
В Android закрепление называется маркером и может представлять собой настраиваемое изображение или задаваемый системой маркер различных цветов. Маркер может содержать информационное окно, которое отображается, когда пользователь касается маркера. В информационном окне отображаются свойства Label
и Address
экземпляра Pin
, однако при необходимости в него можно включить другое содержимое. Тем не менее одновременно может отображаться только одно информационное окно.
В следующем примере кода показан пользовательский отрисовщик для платформы 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);
}
...
}
}
Если пользовательский отрисовщик подключен к новому элементу Xamarin.Forms, метод OnElementChanged
извлекает список пользовательских ПИН-кодов из элемента управления. После получения доступа к экземпляру GoogleMap
вызывается переопределение OnMapReady
. Этот метод регистрирует обработчик для события InfoWindowClick
, которое возникает при щелчке в информационном окне. Подписка на это событие отменяется только в том случае, если изменяется элемент, к которому присоединен отрисовщик. Переопределение OnMapReady
также вызывает метод SetInfoWindowAdapter
, указывая, что методы настройки информационного окна будут предоставлены экземпляром класса CustomMapRenderer
.
Класс CustomMapRenderer
реализует интерфейс GoogleMap.IInfoWindowAdapter
для настройки информационного окна. Этот интерфейс указывает на необходимость реализации следующих методов:
public Android.Views.View GetInfoWindow(Marker marker)
— при вызове этого метода возвращается настраиваемое информационное окно для маркера. Если возвращается значениеnull
, будет использоваться метод отображения окна по умолчанию. Если возвращается объектView
, этот объектView
будет помещен внутрь рамки информационного окна.public Android.Views.View GetInfoContents(Marker marker)
— при вызове этого метода возвращается объектView
с содержимым информационного окна. Этот метод вызывается только в том случае, если методGetInfoWindow
возвращаетnull
. Если этот метод возвращаетnull
, будет использоваться метод отрисовки содержимого окна по умолчанию.
В этом примере приложения выполняется настройка только содержимого информационного окна, поэтому метод GetInfoWindow
возвращает null
.
Настройка маркера
Для настройки значка, который представляет маркер, следует вызвать метод MarkerOptions.SetIcon
. Для этого можно переопределить метод CreateMarker
, который вызывается для каждого объекта Pin
, добавляемого на карту:
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;
}
Этот метод создает новый экземпляр MarkerOption
для каждого экземпляра Pin
. После установки позиции, метки и адреса маркера его значок задается с помощью метода SetIcon
. Этот метод принимает объект BitmapDescriptor
, содержащий необходимые для отрисовки значка данные, с классом BitmapDescriptorFactory
, в котором предоставляются вспомогательные методы, упрощающие создание объекта BitmapDescriptor
. Дополнительные сведения об использовании класса BitmapDescriptorFactory
для настройки маркера см. в статье Настройка маркера.
Примечание.
При необходимости метод GetMarkerForPin
можно вызвать в отрисовщике карты, чтобы извлечь объект Marker
из объекта Pin
.
Настройка информационного окна
Когда пользователь касается маркера, выполняется метод GetInfoContents
при условии, что метод GetInfoWindow
вернул null
. Метод 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;
}
Этот метод возвращает объект View
с содержимым информационного окна. Это реализуется следующим образом:
- Извлекается экземпляр
LayoutInflater
. Он используется для создания экземпляра XML-файла макета в соответствующем объектеView
. - Вызывается метод
GetCustomPin
, который возвращает данные настраиваемого закрепления для информационного окна. - Если свойство
CustomPin.Name
имеет значениеXamarin
, макетXamarinMapInfoWindow
увеличивается. В противном случае увеличивается макетMapInfoWindow
. Это позволяет реализовать сценарии, в которых для разных маркеров могут отображаться разные макеты информационных окон. - Из увеличенного макета извлекаются ресурсы
InfoWindowTitle
иInfoWindowSubtitle
, свойствамText
которых присваиваются соответствующие данные из экземпляраMarker
, при условии, что ресурсы не имеют значенияnull
. - Возвращается экземпляр
View
, который будет отображаться на карте.
Примечание.
Информационное окно не является динамическим объектом View
. Вместо этого ОС Android будет преобразовывать объект View
в статическое растровое изображение и выводить на экран его. Это значит, что информационное окно может реагировать на события щелчка, однако не отвечает на события касания или жестов. При этом отдельные элементы управления в информационном окне не могут реагировать на собственные события щелчка.
Щелчок в информационном окне
Когда пользователь делает щелчок в информационном окне, вызывается событие InfoWindowClick
, которое, в свою очередь, выполняет метод 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);
}
}
Этот метод открывает веб-браузер и выполняет переход по адресу, который хранится в свойстве Url
извлеченного экземпляра CustomPin
для объекта Marker
. Обратите внимание, что этот адрес определяется при создании коллекции CustomPin
в проекте библиотеки .NET Standard.
Дополнительные сведения о настройке экземпляра MapView
см. в статье API карт.
Создание пользовательского отрисовщика на универсальной платформе Windows
На следующих снимках экрана показана карта до и после настройки:
На универсальной платформе Windows (UWP) закрепление называется значком карты и может представлять собой настраиваемое изображение или задаваемое системой изображение по умолчанию. Значок карты может содержать объект UserControl
, который отображается, когда пользователь касается этого значка. Объект UserControl
может отображать любое содержимое, в том числе свойства Label
и Address
экземпляра Pin
.
В следующем примере кода показан настраиваемый отрисовщик для платформы 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);
}
}
}
...
}
}
Метод OnElementChanged
выполняет указанные ниже операции, если настраиваемый отрисовщик подключен к новому элементу Xamarin.Forms:
- Этот метод выполняет очистку коллекции
MapControl.Children
, удаляя с карты любые существующие элементы пользовательского интерфейса, после чего регистрирует обработчик событий для событияMapElementClick
. Это событие вызывается, когда пользователь касается элементаMapElement
на объектеMapControl
или щелкает его. Подписка на него отменяется только в том случае, если изменяется элемент, к которому присоединен отрисовщик. - Каждый закрепить в
customPins
коллекции отображается в правильном географическом расположении на карте следующим образом:- Местоположение закрепления создается в виде экземпляра
Geopoint
. - Создается экземпляр
MapIcon
, представляющий закрепление. - Изображение, представляющее объект
MapIcon
, задается с помощью свойстваMapIcon.Image
. Тем не менее изображение значка карты будет видно не во всех случаях, поскольку его могут скрывать другие элементы на карте. В связи с этим свойствуCollisionBehaviorDesired
значка карты присваивается значениеMapElementCollisionBehavior.RemainVisible
, гарантирующее его постоянную видимость. - Местоположение объекта
MapIcon
задается с помощью свойстваMapIcon.Location
. - В свойстве
MapIcon.NormalizedAnchorPoint
задается примерное местоположение указателя на изображении. Если этому свойству присвоено значение по умолчанию (0,0), которое соответствует левому верхнему углу изображения, при изменении масштаба карты изображение может указывать на другое место. - Экземпляр
MapIcon
добавляется в коллекциюMapControl.MapElements
. Значок карты отображается на объектеMapControl
.
- Местоположение закрепления создается в виде экземпляра
Примечание.
Если для нескольких значков карты используется одно и то же изображение, для оптимальной производительности экземпляр RandomAccessStreamReference
следует объявлять на уровне страницы или приложения.
Отображение объекта UserControl
Когда пользователь касается значка карты, выполняется метод OnMapElementClick
. Этот метод показан в следующем примере кода:
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;
}
}
}
Этот метод создает экземпляр UserControl
, который отображает информацию о закреплении. Это реализуется следующим образом:
- Извлекается экземпляр
MapIcon
. - Вызывается метод
GetCustomPin
, возвращающий данные настраиваемого закрепления, которые требуется отобразить. - Создается экземпляр
XamarinMapOverlay
для отображения данных настраиваемого закрепления. Этот класс представляет пользовательский элемент управления. - Географическое расположение, в котором отображается экземпляр
XamarinMapOverlay
на объектеMapControl
, создается в виде экземпляраGeopoint
. - Экземпляр
XamarinMapOverlay
добавляется в коллекциюMapControl.Children
. Эта коллекция содержит элементы пользовательского интерфейса XAML, которые будут отображаться на карте. - Географическое местоположение
XamarinMapOverlay
экземпляра на карте задается посредством вызова методаSetLocation
. - Относительное положение экземпляра
XamarinMapOverlay
, которое соответствует указанному местоположению, задается посредством вызова методаSetNormalizedAnchorPoint
. Благодаря этому гарантируется, что при изменении масштаба карты экземплярXamarinMapOverlay
всегда будет отображаться в правильном местоположении.
Также, если на карте уже отображается информация о закреплении, при касании карты экземпляр XamarinMapOverlay
удаляется из коллекции MapControl.Children
.
Касание кнопки "Сведения"
Когда пользователь касается кнопки Сведения в пользовательском элементе управления XamarinMapOverlay
, вызывается событие Tapped
, которое, в свою очередь, вызывает метод OnInfoButtonTapped
:
private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}
Этот метод открывает веб-браузер и выполняет переход по адресу, который хранится в свойстве Url
экземпляра CustomPin
. Обратите внимание, что этот адрес определяется при создании коллекции CustomPin
в проекте библиотеки .NET Standard.
Дополнительные сведения о настройке экземпляра MapControl
см. в статье Общие сведения о картах и местоположениях на сайте MSDN.