自定义图钉
本文介绍如何为地图控件创建自定义呈现器,该控件显示带有自定义图钉的本机地图以及每个平台上图钉数据的自定义视图。
每个 Xamarin.Forms 视图都有一个附带的呈现器,适用于创建本机控件实例的各个平台。 当 iOS 中的 Xamarin.Forms 应用程序呈现 Map
时,将实例化 MapRenderer
类,而该操作又会实例化本机 MKMapView
控件。 在 Android 平台上,MapRenderer
类实例化本机 MapView
控件。 在通用 Windows 平台 (UWP) 上,MapRenderer
类实例化本机 MapControl
。 有关 Xamarin.Forms 控件映射到的呈现器和本机控件类的详细信息,请参阅呈现器基类和本机控件。
下图说明了 Map
和实现它的相应本机控件之间的关系:
通过在每个平台上为 Map
创建自定义呈现器,可以使用呈现过程来实现特定于平台的自定义。 执行此操作的过程如下:
现在将依次讨论每个项,以实现 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
控件位置的命名空间并使用命名空间前缀,可以在 .NET Standard 库项目的 XAML 中引用该控件。 下面的代码示例演示 XAML 页可以如何使用 CustomMap
控件:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<local:CustomMap x:Name="customMap"
MapType="Street" />
</ContentPage>
local
命名空间前缀可以命名为任何内容。 但是,clr-namespace
和 assembly
值必须与自定义地图的详细信息相匹配。 声明命名空间后,前缀用于引用自定义地图。
下面的代码示例演示 C# 页可以如何使用 CustomMap
控件:
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
方法定位地图视图,该方法通过从 Position
和 Distance
创建 MapSpan
来更改地图的位置和缩放级别。
现在可以向每个应用程序项目添加自定义呈现器,以自定义本机地图控件。
在每个平台上创建自定义呈现器
创建自定义呈现器类的过程如下所示:
- 创建呈现自定义地图的
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
属性访问平台上使用的对本机控件的类型化引用。 此外,可以通过 Element
属性获取正在呈现的 Xamarin.Forms 控件的引用。
订阅 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 上,图钉称为“注释”,可以是自定义映像,也可以是不同颜色的系统定义的图钉。 注释可以选择性地显示标注,以在用户选择注释时作出响应。 标注显示 Pin
实例的 Label
和 Address
属性,以及可选的左和右附件视图。 上面的屏幕截图中,左附件视图是猴子图像,而右附件视图是“信息”按钮。
以下代码示例展示了适用于 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;
}
}
// ...
}
}
如果自定义呈现器附加到新的 Xamarin.Forms 元素,则 OnElementChanged
方法执行 MKMapView
实例的以下配置:
GetViewForAnnotation
属性设置为GetViewForAnnotation
方法. 此方法在地图上显示注释位置时调用,用于在显示前自定义注释。- 已注册用于
CalloutAccessoryControlTapped
、DidSelectAnnotationView
和DidDeselectAnnotationView
事件的事件处理程序。 这些事件在以下情况下触发:用户点击标注中的右附件、用户选择和取消选择注释。 仅当呈现器附加到的元素更改时,才取消订阅事件。
显示注释
GetViewForAnnotation
方法在地图上显示注释位置时调用,用于在显示前自定义注释。 注释分为两个部分:
MkAnnotation
- 包括标题、副标题和注释位置。MkAnnotationView
- 包含表示该注释的图像,和(可选)用户点击注释时显示的标注。
GetViewForAnnotation
方法接受包含注释数据并返回用于显示在地图上的 MKAnnotationView
的 IMKAnnotation
,如以下代码示例所示:
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
类使用Name
和Url
属性扩展MKAnnotationView
类,这些属性对应CustomPin
实例中相同的属性。 如果注释为null
,则创建CustomMKAnnotationView
的新实例:CustomMKAnnotationView.Image
属性设置为将表示地图上的注释的图像。CustomMKAnnotationView.CalloutOffset
属性设置为CGPoint
,它指定在注释上方将标注居中。CustomMKAnnotationView.LeftCalloutAccessoryView
属性设置为猴子图像,该图像将出现在注释标题和地址的左侧。CustomMKAnnotationView.RightCalloutAccessoryView
属性设置为“信息”按钮,其将出现在注释标题和地址的右侧。CustomMKAnnotationView.Name
属性设置为GetCustomPin
方法返回的CustomPin.Name
属性。 这使得注释能被识别,以便需要时其标注可以进一步自定义。CustomMKAnnotationView.Url
属性设置为GetCustomPin
方法返回的CustomPin.Url
属性。 用户点击右标注附件视图中显示的按钮时,将导航到该 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);
}
}
如果所选注释将其 Name
属性设置为 Xamarin
,则此方法通过将 UIView
实例添加到包含 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));
}
}
此方法打开 Web 浏览器并导航到 CustomMKAnnotationView.Url
属性中存储的地址。 请注意,地址是在 .NET Standard 库项目中创建 CustomPin
集合时定义的。
取消选择注释
注释显示且用户点击地图时,触发 DidDeselectAnnotationView
事件,该事件进而执行 OnDidDeselectAnnotationView
方法:
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
此方法确保在未选择现有标注时,标注的扩展部分(Xamarin 徽标的图像)也将停止显示,并释放其资源。
有关自定义 MKMapView
实例的详细信息,请参阅 iOS Maps。
在 Android 上创建自定义呈现器
下面的屏幕截图显示自定义前后的地图:
在 Android 上,图钉称为“标记”,可以是自定义图像,也可以是不同颜色的系统定义的标记。 标记可以显示信息窗口,在用户点击标记时作出响应。 信息窗口显示 Pin
实例的 Label
和 Address
属性,并可以通过自定义包含其他内容。 但是,一次只可以显示一个信息窗口。
以下代码示例展示了适用于 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;
}
此方法为每个 Pin
实例创建新的 MarkerOption
实例。 设置位置、标签和标记的地址后,使用 SetIcon
方法设置其图标。 此方法采用 BitmapDescriptor
对象,该对象包含呈现图标的必要数据,BitmapDescriptorFactory
类提供帮助程序方法以简化 BitmapDescriptor
的创建。 有关使用 BitmapDescriptorFactory
类自定义标记的详细信息,请参阅自定义标记。
注意
如果需要,可以在地图呈现器中调用 GetMarkerForPin
方法以检索 Pin
中的 Marker
。
自定义信息窗口
用户点击标记时,如果 GetInfoWindow
方法返回 null
,则执行 GetInfoContents
方法。 下面的代码示例说明 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
资源,如果资源不是null
,则Text
属性设置为Marker
实例中的相应数据。 - 返回
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);
}
}
此方法打开 Web 浏览器并导航到检索到的 Marker
的 CustomPin
实例的 Url
属性中存储的地址。 请注意,地址是在 .NET Standard 库项目中创建 CustomPin
集合时定义的。
有关自定义 MapView
实例的详细信息,请参阅地图 API。
在通用 Windows 平台上创建自定义呈现器
下面的屏幕截图显示自定义前后的地图:
在 UWP 上,图钉称为“地图图标”,可以是自定义图像,也可以是系统定义的默认图像。 地图图标可以显示 UserControl
,在用户点击地图图标时显示。 UserControl
可以显示任何内容,包括 Pin
实例的 Label
和 Address
属性。
以下代码示例显示 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);
}
}
}
...
}
}
如果自定义呈现器附加到新的 Xamarin.Forms 元素,则 OnElementChanged
方法执行以下操作:
- 在注册
MapElementClick
事件的事件处理程序之前,它清除MapControl.Children
集合以删除地图中的任何现有的用户界面元素。 用户点击或单击MapControl
上的MapElement
时,触发此事件,并且仅当呈现器附加到的元素更改时才取消订阅。 customPins
集合中的每个图钉显示在地图的正确地理位置上,如下所示:- 图钉位置创建为
Geopoint
实例。 - 创建
MapIcon
实例以表示图钉。 - 通过设置
MapIcon.Image
属性来指定用于表示MapIcon
的图像。 但是,并不能保证始终显示地图图标的图像,因为地图上的其他元素可能遮挡该图像。 因此,地图图标的CollisionBehaviorDesired
属性设置为MapElementCollisionBehavior.RemainVisible
,以确保它始终显示。 - 通过设置
MapIcon.Location
属性指定MapIcon
的位置。 MapIcon.NormalizedAnchorPoint
属性设置为图像上指针的近似位置。 如果此属性保留其默认值 (0,0)(表示图像的左上角),地图缩放级别的更改可能导致图像指向其他位置。- 向
MapControl.MapElements
集合添加MapIcon
实例。 这导致地图图标显示在MapControl
上。
- 图钉位置创建为
注意
将同一图像用于多个地图图标时,为了获得最佳性能,应该在页面或应用程序级别声明 RandomAccessStreamReference
实例。
显示用户控件
用户点击地图图标时,执行 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
实例以显示自定义的图钉数据。 此类是用户控件。 - 将在
MapControl
上显示XamarinMapOverlay
实例的地理位置创建为Geopoint
实例。 - 向
MapControl.Children
集合添加XamarinMapOverlay
实例。 此集合包含将在地图上显示的 XAML 用户界面元素。 - 通过调用
SetLocation
方法在地图上设置XamarinMapOverlay
实例的地理位置。 - 通过调用
SetNormalizedAnchorPoint
方法来设置对应特定位置的XamarinMapOverlay
实例的相对位置。 这可确保地图缩放级别的更改导致XamarinMapOverlay
实例始终在正确位置显示。
或者,如果地图上已显示有关图钉的信息,点击地图以移除 MapControl.Children
集合中的 XamarinMapOverlay
实例。
点击“信息”按钮
用户点击 XamarinMapOverlay
用户控件中的“信息”按钮时,触发 Tapped
事件,然后该事件执行 OnInfoButtonTapped
方法:
private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}
此方法打开 Web 浏览器并导航到 CustomPin
实例的 Url
属性中存储的地址。 请注意,地址是在 .NET Standard 库项目中创建 CustomPin
集合时定义的。
有关自定义 MapControl
实例的详细信息,请参阅 MSDN 上的地图和位置概述。