Xamarin Forms Maps Custom Android Renderer GetMapAsync 不调用 OnMapReady
Xamarin Forms Maps Custom Android Renderer GetMapAsync Not Calling OnMapReady
更新
我 运行 遇到 GetMapAsync 问题 未调用 OnMapReady 我可以通过检查地图是否为 null 来抑制错误,然后地图将加载非自定义标记。如果我随后稍微移动地图,OnMapReady 将加载,然后将显示自定义标记。下面是我的自定义渲染器 class,如果有人 运行 解决了这个问题,我将非常感谢您的解决方案。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Views;
using Android.Widget;
using BSi.Mobile.Events.Controls;
using BSi.Mobile.Events.Droid.Renderers;
using BSi.Mobile.Events.Events;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using View = Xamarin.Forms.View;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace BSi.Mobile.Events.Droid.Renderers
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
IList<CustomPin> customPins;
private CustomPin selectedPin;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
map.MarkerClick -= OnMarkerClick;
map.MapClick -= OnMapClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
selectedPin = formsMap.SelectedPin;
((MapView)Control).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.MarkerClick += OnMarkerClick;
map.MapClick += OnMapClick;
map.SetInfoWindowAdapter(this);
SetMapMarkers();
}
private void SetMapMarkers()
{
map.Clear();
foreach (var pin in customPins)
{
addMarker(pin, false);
}
isDrawn = true;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn && map != null)
{
SetMapMarkers();
}
else if (e.PropertyName.EndsWith("SelectedPin") && map != null)
{
var customMap = sender as CustomMap;
selectedPin = customMap?.SelectedPin;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
}
private void addMarker(CustomPin pin, bool isSelected)
{
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);
switch (pin.Id)
{
case "Convention":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.star));
break;
case "cafe":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.cafe));
break;
case "bar":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.bar));
break;
default:
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.restaurant));
break;
}
var selectedMarker = map.AddMarker(marker);
if (isSelected)
selectedMarker.ShowInfoWindow();
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
//Catches Exception incase user needs to update google play services
try
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
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 LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
switch (customPin.Id)
{
case "Conference":
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
default:
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
var infoPhone = view.FindViewById<TextView>(Resource.Id.InfoWindowPhone);
var infoWebsite = view.FindViewById<TextView>(Resource.Id.InfoWindowWebsite);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
else
{
infoTitle.Visibility = ViewStates.Gone;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
else
{
infoSubtitle.Visibility = ViewStates.Gone;
}
if (infoPhone != null && customPin.Phone != null)
{
infoPhone.Text = customPin.Phone;
}
else
{
infoPhone.Visibility = ViewStates.Gone;
}
if (infoWebsite != null && customPin.Url != null)
{
infoWebsite.Text = customPin.Url;
}
else
{
infoWebsite.Visibility = ViewStates.Gone;
}
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;
}
private void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
{
e.Handled = false;
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(new Pin { Position = new Position(e.Marker.Position.Latitude, e.Marker.Position.Longitude), Label = e.Marker.Title});
}
private void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
{
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(null);
}
}
}
我找到了解决此问题的解决方法我移动了我在其自己的函数中设置地图标记的代码,从那里我在 OnElementPropertyChanged 中调用该函数,就像正常执行一样。这会在地图移动时设置标记。为了在加载地图时加载自定义标记,我还在 onMapReady 中调用了设置标记。我的上面的代码现在反映了一个可行的解决方案,我希望我已经帮助了可能 运行 解决这个问题的任何人干杯!
更新
我 运行 遇到 GetMapAsync 问题 未调用 OnMapReady 我可以通过检查地图是否为 null 来抑制错误,然后地图将加载非自定义标记。如果我随后稍微移动地图,OnMapReady 将加载,然后将显示自定义标记。下面是我的自定义渲染器 class,如果有人 运行 解决了这个问题,我将非常感谢您的解决方案。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Views;
using Android.Widget;
using BSi.Mobile.Events.Controls;
using BSi.Mobile.Events.Droid.Renderers;
using BSi.Mobile.Events.Events;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using View = Xamarin.Forms.View;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace BSi.Mobile.Events.Droid.Renderers
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
IList<CustomPin> customPins;
private CustomPin selectedPin;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
map.MarkerClick -= OnMarkerClick;
map.MapClick -= OnMapClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
selectedPin = formsMap.SelectedPin;
((MapView)Control).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.MarkerClick += OnMarkerClick;
map.MapClick += OnMapClick;
map.SetInfoWindowAdapter(this);
SetMapMarkers();
}
private void SetMapMarkers()
{
map.Clear();
foreach (var pin in customPins)
{
addMarker(pin, false);
}
isDrawn = true;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn && map != null)
{
SetMapMarkers();
}
else if (e.PropertyName.EndsWith("SelectedPin") && map != null)
{
var customMap = sender as CustomMap;
selectedPin = customMap?.SelectedPin;
if (selectedPin != null)
{
addMarker(selectedPin, true);
}
}
}
private void addMarker(CustomPin pin, bool isSelected)
{
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);
switch (pin.Id)
{
case "Convention":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.star));
break;
case "cafe":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.cafe));
break;
case "bar":
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.bar));
break;
default:
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.restaurant));
break;
}
var selectedMarker = map.AddMarker(marker);
if (isSelected)
selectedMarker.ShowInfoWindow();
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
//Catches Exception incase user needs to update google play services
try
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
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 LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
switch (customPin.Id)
{
case "Conference":
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
default:
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
break;
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
var infoPhone = view.FindViewById<TextView>(Resource.Id.InfoWindowPhone);
var infoWebsite = view.FindViewById<TextView>(Resource.Id.InfoWindowWebsite);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
else
{
infoTitle.Visibility = ViewStates.Gone;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
else
{
infoSubtitle.Visibility = ViewStates.Gone;
}
if (infoPhone != null && customPin.Phone != null)
{
infoPhone.Text = customPin.Phone;
}
else
{
infoPhone.Visibility = ViewStates.Gone;
}
if (infoWebsite != null && customPin.Url != null)
{
infoWebsite.Text = customPin.Url;
}
else
{
infoWebsite.Visibility = ViewStates.Gone;
}
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;
}
private void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
{
e.Handled = false;
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(new Pin { Position = new Position(e.Marker.Position.Latitude, e.Marker.Position.Longitude), Label = e.Marker.Title});
}
private void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
{
App.EventAggregator?.GetEvent<MapClickedEvent>().Publish(null);
}
}
}
我找到了解决此问题的解决方法我移动了我在其自己的函数中设置地图标记的代码,从那里我在 OnElementPropertyChanged 中调用该函数,就像正常执行一样。这会在地图移动时设置标记。为了在加载地图时加载自定义标记,我还在 onMapReady 中调用了设置标记。我的上面的代码现在反映了一个可行的解决方案,我希望我已经帮助了可能 运行 解决这个问题的任何人干杯!