Xamarin.forms Xamarin形成自定义地图Pin
在我正在开发的一个应用程序中,我需要使用自定义的地图别针,我遵循了Xamarin的指南,并借用了他们的示例代码来尝试制作我自己的示例 它在某种程度上可以使“信息”窗口实际更新为自定义布局,但地图图钉从未更改 我的CustomMapRenderer:Xamarin.forms Xamarin形成自定义地图Pin,xamarin.forms,maps,customization,Xamarin.forms,Maps,Customization,在我正在开发的一个应用程序中,我需要使用自定义的地图别针,我遵循了Xamarin的指南,并借用了他们的示例代码来尝试制作我自己的示例 它在某种程度上可以使“信息”窗口实际更新为自定义布局,但地图图钉从未更改 我的CustomMapRenderer: using System; using System.Collections.Generic; using System.ComponentModel; using Android.Content; using Android.Gms.Maps;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using WorkingWithMaps.Droid.Renderers;
using WorkingWithMaps;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace WorkingWithMaps.Droid.Renderers
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
List<CustomPin> customPins;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
void IOnMapReadyCallback.OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.SetInfoWindowAdapter(this);
map.InfoWindowClick += OnInfoWindowClick;
this.NativeMap = googleMap;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
map.Clear();
foreach (var pin in customPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
map.AddMarker(marker);
}
isDrawn = true;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
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);
}
}
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.Id == "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;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}
}
抛出异常
Github存储库:首先,我想提供两个链接,帮助我理解这个问题。谢谢你们。 及 最新的Xamarin地图打破了一个元素属性随VisibleRegion的变化。他们定义MapRenderer现在实现了IOnMapReadyCallback,并以某种方式破坏了OnElementPropertyChanged(我没有研究如何以及为什么)。正如你们在我提供的链接中看到的,有两种方法你们可以选择。保持渲染器实现IOnMapReadyCallback与否。当我保留IOnMapReadyCallback时,我开始获得两个pin——一个是最上面的一个——我们的自定义pin和常规pin。我没有进一步挖掘这是如何发生的,并删除了IOnMapReadyCallback。之后事情变得非常简单,因为如果让Xamarin处理并创建NativeMap,您可以删除一些代码并使渲染器更简单 在我发布代码之前,我还想提一下,当我修复它时,应用程序开始崩溃,出现OutOfMemory异常,我发现你的pin图像宽度为2000像素。我把它改成了40。代码如下:
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter//, IOnMapReadyCallback
{
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
}
bool isMapReady;
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (!isMapReady && (NativeMap != null))
{
NativeMap.SetInfoWindowAdapter(this);
NativeMap.InfoWindowClick += OnInfoWindowClick;
isMapReady = true;
}
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
NativeMap.Clear();
foreach (var pin in ((CustomMap)Element).CustomPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn = true;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
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);
}
}
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.Id == "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;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in ((CustomMap)Element).CustomPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}
public类CustomMapRenderer:MapRenderer,GoogleMap.IInfoWindowAdapter//,IOnMapReadyCallback
{
布尔是拉文;
受保护的覆盖无效OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs e)
{
基础。一个要素发生变化(e);
if(e.OldElement!=null)
{
NativeMap.InfoWindowClick-=OnInfoWindowClick;
}
}
布尔·伊斯马普雷迪;
受保护的覆盖无效OnElementPropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(发送方,e);
如果(!isMapReady&(NativeMap!=null))
{
NativeMap.SetInfoWindowAdapter(此);
NativeMap.InfoWindowClick+=OnInfoWindowClick;
isMapReady=true;
}
if(e.PropertyName.Equals(“VisibleRegion”)&&&!isDrawn)
{
NativeMap.Clear();
foreach(var pin in((CustomMap)元素).CustomPins)
{
var marker=新的MarkerOptions();
marker.SetPosition(新板条(针.针.位置.纬度,针.针.位置.经度));
marker.SetTitle(pin.pin.Label);
标记器固定接头(引脚地址);
SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn=true;
}
}
受保护覆盖仅限无效布局(布尔值已更改、整数l、整数t、整数r、整数b)
{
仅基础布局(已更改,l、t、r、b);
如果(更改)
{
isDrawn=假;
}
}
无效OnInfoWindowClick(对象发送者,GoogleMap.InfoWindowClickEventArgs e)
{
var customPin=GetCustomPin(即标记);
如果(customPin==null)
{
抛出新异常(“未找到自定义pin”);
}
如果(!string.IsNullOrWhiteSpace(customPin.Url))
{
var url=Android.Net.Uri.Parse(customPin.url);
var intent=新的intent(intent.ActionView,url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(标记器)
{
var inflater=Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService)作为Android.Views.LayoutInflater;
如果(充气机!=null)
{
Android.Views.View;
var customPin=GetCustomPin(标记);
如果(customPin==null)
{
抛出新异常(“未找到自定义pin”);
}
如果(customPin.Id==“Xamarin”)
{
视图=充气机.Inflate(Resource.Layout.xamarinMapInfo窗口,null);
}
其他的
{
视图=充气机.Inflate(Resource.Layout.MapInfo窗口,null);
}
var infoTitle=view.findviewbyd(Resource.Id.InfoWindowTitle);
var infoSubtitle=view.findviewbyd(Resource.Id.InfoWindowSubtitle);
if(infoTitle!=null)
{
infoTitle.Text=marker.Title;
}
if(infoSubtitle!=null)
{
infoSubtitle.Text=marker.Snippet;
}
返回视图;
}
返回null;
}
public Android.Views.View GetInfoWindow(标记器)
{
返回null;
}
CustomPin GetCustomPin(标记注释)
{
变量位置=新位置(annotation.position.Latitude,annotation.position.Longitude);
foreach(var pin in((CustomMap)元素).CustomPins)
{
if(pin.pin.Position==位置)
{
回位销;
}
}
返回null;
}
}
首先,我想提供两个帮助我理解问题的链接。谢谢你们。 及 最新的Xamarin地图打破了一个元素属性随VisibleRegion的变化。他们定义MapRenderer现在实现了IOnMapReadyCallback,并以某种方式破坏了OnElementPropertyChanged(我没有研究如何以及为什么)。正如你们在我提供的链接中看到的,有两种方法你们可以选择。保持渲染器实现IOnMapReadyCallback与否。当我保留IOnMapReadyCallback时,我开始获得两个pin——一个是最上面的一个——我们的自定义pin和常规pin。我没有进一步挖掘这是如何发生的,并删除了IOnMapReadyCallback。在那之后事情变得非常简单因为如果你让Xamarin来处理
var currentpos = await maplocator.GetPositionAsync(1000);
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter//, IOnMapReadyCallback
{
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
}
bool isMapReady;
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (!isMapReady && (NativeMap != null))
{
NativeMap.SetInfoWindowAdapter(this);
NativeMap.InfoWindowClick += OnInfoWindowClick;
isMapReady = true;
}
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
NativeMap.Clear();
foreach (var pin in ((CustomMap)Element).CustomPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn = true;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
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);
}
}
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.Id == "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;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in ((CustomMap)Element).CustomPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}