Xamarin.Forms/Xamarin.Forms.Maps.Android/MapRenderer.cs

1028 строки
23 KiB
C#
Исходник Обычный вид История

2016-03-22 23:02:25 +03:00
using System;
using System.Collections;
2016-03-22 23:02:25 +03:00
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Android;
[All] Page embedding (#1124) * Fix broken SetTextAppearance call when targeting below API 23 * Enable embedding XF apps in Android * VS didn't save the project file * Allow retrieving rendered XF VisualElements for use in UWP apps * Prevent XF from slaughtering the menu bar in embedded scenarios * ? * Post-rebase fixups * Create embedding test project for Android * Create iOS test bed project * Attempting to get functional embedded project * Android and iOS functional testbed projects * Basic embedding app on UWP * First pass at handling navigation to Forms page from UWP * Fleshed out example, added parameter checks, sealed wrapper class * Create test page and load it from UWP * Make DisplayAlert/DisplayActionSheet independent of Platform instance on UWP * Set up test page for alerts/actionsheets on Android * Move DisplayAlert/ActionSheet code to a helper class so embedded Forms can use it * VS didn't save my changes * Add test page for alerts and action sheets * Get FrameworkElement from renderer * Page -> ContentPage * Can now display the webview embedded page * Example working on iOS * Repro of crash * Remove the IStartActivityForResult dependency from FormsWebChromeClient * Created repro * Remove need for Forms.Context to create renderers * Remove Forms.Context dependencies in ScrollViewRenderer * Remove Forms.Context dependencies in FrameRenderer * Remove Forms.Context dependencies * Remove Context/Forms.Context dependencies in Drawable subclasses * Remove some more Forms.Context dependencies * CellAdapter no longer dependent on Forms.Context * Obsolete ToAndroid using Forms.Context * Remove Forms.Context dependencies in ResourceManager * Remove need for Forms.Context for SupportsProgress * Remove Forms.Context dependency for setting titlebar visibility * Remove Forms.Context dependencies in GetAccentColor * Some comments about caching * Remove Forms.Context dependencies for AndroidPlatformServices and ResourceProvider * Remove Forms.Context dependencies in Maps * Disabled warnings for Forms.Context in CustomRenderers * Filter logs so we don't get those annoying "parked" messages * Filter alerts/actionsheets/activityindicator by context * Clean up constructor debugging messages * Remove old TODOs * Set up test page for Device.Openuri * Make AndroidDeviceInfo more resilient to multiple activity scenarios * Use parameter instead of member * Add missing parameterless constructors for embedded fragment wrappers * Fix multiple popup subscriptions for embedded context * Remove UpdateGlobalContext and Page Context attached property * Obsolete static reference to Context * Warnings as errors in embedded test bed projects * Comment cleanup * Clean up unused code * Obsolete old constructor for DefaultRenderer * Make sure embedded fragment wrappers handle disposing the platform * Revert to old DependencyService registration for ResourceProvider; use ApplicationContext for ResourceProvider; Comments for everything happing in Forms.SetupInit * Remove old TODO * Make PopupManager take the correct type instead of casting a bunch * Update docs * Add missing nuspec entry for FormsEmbeddedPageWrapper * Post-rebase cleanup * Update docs * Disable XF target validation so package restore works * Restore InputTransparent handling lost in rebase * Restore parameter lost during rebase * Finalize the list of subscriptions to avoid 'modified collection' errors * Avoid double-fetching Context * Fix "with you package" typo
2017-10-09 20:51:55 +03:00
using Android.Content;
using Android.Content.PM;
2016-03-22 23:02:25 +03:00
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.OS;
#if __ANDROID_29__
using AndroidX.Core.Content;
#else
using Android.Support.V4.Content;
#endif
2016-03-22 23:02:25 +03:00
using Java.Lang;
using Xamarin.Forms.Internals;
2016-03-22 23:02:25 +03:00
using Xamarin.Forms.Platform.Android;
using Math = System.Math;
using APolyline = Android.Gms.Maps.Model.Polyline;
using APolygon = Android.Gms.Maps.Model.Polygon;
using ACircle = Android.Gms.Maps.Model.Circle;
2016-03-22 23:02:25 +03:00
namespace Xamarin.Forms.Maps.Android
{
2018-05-15 18:16:52 +03:00
public class MapRenderer : ViewRenderer<Map, MapView>, GoogleMap.IOnCameraMoveListener, IOnMapReadyCallback
2016-03-22 23:02:25 +03:00
{
const string MoveMessageName = "MapMoveToRegion";
2016-03-22 23:02:25 +03:00
static Bundle s_bundle;
bool _disposed;
bool _init = true;
2016-03-22 23:02:25 +03:00
List<Marker> _markers;
List<APolyline> _polylines;
List<APolygon> _polygons;
List<ACircle> _circles;
2016-03-22 23:02:25 +03:00
[All] Page embedding (#1124) * Fix broken SetTextAppearance call when targeting below API 23 * Enable embedding XF apps in Android * VS didn't save the project file * Allow retrieving rendered XF VisualElements for use in UWP apps * Prevent XF from slaughtering the menu bar in embedded scenarios * ? * Post-rebase fixups * Create embedding test project for Android * Create iOS test bed project * Attempting to get functional embedded project * Android and iOS functional testbed projects * Basic embedding app on UWP * First pass at handling navigation to Forms page from UWP * Fleshed out example, added parameter checks, sealed wrapper class * Create test page and load it from UWP * Make DisplayAlert/DisplayActionSheet independent of Platform instance on UWP * Set up test page for alerts/actionsheets on Android * Move DisplayAlert/ActionSheet code to a helper class so embedded Forms can use it * VS didn't save my changes * Add test page for alerts and action sheets * Get FrameworkElement from renderer * Page -> ContentPage * Can now display the webview embedded page * Example working on iOS * Repro of crash * Remove the IStartActivityForResult dependency from FormsWebChromeClient * Created repro * Remove need for Forms.Context to create renderers * Remove Forms.Context dependencies in ScrollViewRenderer * Remove Forms.Context dependencies in FrameRenderer * Remove Forms.Context dependencies * Remove Context/Forms.Context dependencies in Drawable subclasses * Remove some more Forms.Context dependencies * CellAdapter no longer dependent on Forms.Context * Obsolete ToAndroid using Forms.Context * Remove Forms.Context dependencies in ResourceManager * Remove need for Forms.Context for SupportsProgress * Remove Forms.Context dependency for setting titlebar visibility * Remove Forms.Context dependencies in GetAccentColor * Some comments about caching * Remove Forms.Context dependencies for AndroidPlatformServices and ResourceProvider * Remove Forms.Context dependencies in Maps * Disabled warnings for Forms.Context in CustomRenderers * Filter logs so we don't get those annoying "parked" messages * Filter alerts/actionsheets/activityindicator by context * Clean up constructor debugging messages * Remove old TODOs * Set up test page for Device.Openuri * Make AndroidDeviceInfo more resilient to multiple activity scenarios * Use parameter instead of member * Add missing parameterless constructors for embedded fragment wrappers * Fix multiple popup subscriptions for embedded context * Remove UpdateGlobalContext and Page Context attached property * Obsolete static reference to Context * Warnings as errors in embedded test bed projects * Comment cleanup * Clean up unused code * Obsolete old constructor for DefaultRenderer * Make sure embedded fragment wrappers handle disposing the platform * Revert to old DependencyService registration for ResourceProvider; use ApplicationContext for ResourceProvider; Comments for everything happing in Forms.SetupInit * Remove old TODO * Make PopupManager take the correct type instead of casting a bunch * Update docs * Add missing nuspec entry for FormsEmbeddedPageWrapper * Post-rebase cleanup * Update docs * Disable XF target validation so package restore works * Restore InputTransparent handling lost in rebase * Restore parameter lost during rebase * Finalize the list of subscriptions to avoid 'modified collection' errors * Avoid double-fetching Context * Fix "with you package" typo
2017-10-09 20:51:55 +03:00
public MapRenderer(Context context) : base(context)
{
AutoPackage = false;
}
2017-10-20 23:02:16 +03:00
[Obsolete("This constructor is obsolete as of version 2.5. Please use MapRenderer(Context) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public MapRenderer()
{
AutoPackage = false;
}
protected Map Map => Element;
2016-03-22 23:02:25 +03:00
protected GoogleMap NativeMap;
internal static Bundle Bundle
{
set { s_bundle = value; }
}
2016-03-22 23:02:25 +03:00
public override SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint)
2016-03-22 23:02:25 +03:00
{
return new SizeRequest(new Size(Context.ToPixels(40), Context.ToPixels(40)));
2016-03-22 23:02:25 +03:00
}
protected override MapView CreateNativeControl()
{
return new MapView(Context);
}
protected override void Dispose(bool disposing)
2016-03-22 23:02:25 +03:00
{
if (_disposed)
{
return;
}
2016-03-22 23:02:25 +03:00
_disposed = true;
if (disposing)
{
if (Element != null)
{
MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
((ObservableCollection<Pin>)Element.Pins).CollectionChanged -= OnPinCollectionChanged;
foreach (Pin pin in Element.Pins)
{
pin.PropertyChanged -= PinOnPropertyChanged;
}
((ObservableCollection<MapElement>)Element.MapElements).CollectionChanged -= OnMapElementCollectionChanged;
foreach (MapElement child in Element.MapElements)
{
child.PropertyChanged -= MapElementPropertyChanged;
}
}
2016-03-22 23:02:25 +03:00
if (NativeMap != null)
{
NativeMap.MyLocationEnabled = false;
NativeMap.SetOnCameraMoveListener(null);
NativeMap.MarkerClick -= OnMarkerClick;
NativeMap.InfoWindowClick -= OnInfoWindowClick;
NativeMap.MapClick -= OnMapClick;
NativeMap.Dispose();
NativeMap = null;
}
Control?.OnDestroy();
}
base.Dispose(disposing);
}
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
MapView oldMapView = Control;
2016-03-22 23:02:25 +03:00
MapView mapView = CreateNativeControl();
mapView.OnCreate(s_bundle);
mapView.OnResume();
SetNativeControl(mapView);
if (e.OldElement != null)
{
Map oldMapModel = e.OldElement;
2016-03-22 23:02:25 +03:00
((ObservableCollection<Pin>)oldMapModel.Pins).CollectionChanged -= OnPinCollectionChanged;
foreach (Pin pin in oldMapModel.Pins)
{
pin.PropertyChanged -= PinOnPropertyChanged;
}
((ObservableCollection<MapElement>)oldMapModel.MapElements).CollectionChanged -= OnMapElementCollectionChanged;
foreach (MapElement child in oldMapModel.MapElements)
{
child.PropertyChanged -= MapElementPropertyChanged;
}
MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
2016-03-22 23:02:25 +03:00
if (NativeMap != null)
{
NativeMap.SetOnCameraMoveListener(null);
NativeMap.MarkerClick -= OnMarkerClick;
NativeMap.InfoWindowClick -= OnInfoWindowClick;
NativeMap.MapClick -= OnMapClick;
NativeMap = null;
2016-03-22 23:02:25 +03:00
}
oldMapView.Dispose();
}
Control.GetMapAsync(this);
MessagingCenter.Subscribe<Map, MapSpan>(this, MoveMessageName, OnMoveToRegionMessage, Map);
2016-03-22 23:02:25 +03:00
((INotifyCollectionChanged)Map.Pins).CollectionChanged += OnPinCollectionChanged;
((INotifyCollectionChanged)Map.MapElements).CollectionChanged += OnMapElementCollectionChanged;
2016-03-22 23:02:25 +03:00
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
2016-03-22 23:02:25 +03:00
{
base.OnElementPropertyChanged(sender, e);
2016-03-22 23:02:25 +03:00
if (e.PropertyName == Map.MapTypeProperty.PropertyName)
{
2016-03-22 23:02:25 +03:00
SetMapType();
return;
}
GoogleMap gmap = NativeMap;
2016-03-22 23:02:25 +03:00
if (gmap == null)
{
2016-03-22 23:02:25 +03:00
return;
}
2016-03-22 23:02:25 +03:00
if (e.PropertyName == Map.IsShowingUserProperty.PropertyName)
{
SetUserVisible();
}
2016-03-22 23:02:25 +03:00
else if (e.PropertyName == Map.HasScrollEnabledProperty.PropertyName)
{
2016-03-22 23:02:25 +03:00
gmap.UiSettings.ScrollGesturesEnabled = Map.HasScrollEnabled;
}
else if (e.PropertyName == Map.HasZoomEnabledProperty.PropertyName)
{
2016-03-22 23:02:25 +03:00
gmap.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled;
gmap.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
2016-03-22 23:02:25 +03:00
{
base.OnLayout(changed, l, t, r, b);
2016-03-22 23:02:25 +03:00
if (_init)
{
if (NativeMap != null)
{
MoveToRegion(Element.LastMoveToRegion, false);
OnPinCollectionChanged(Element.Pins, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnMapElementCollectionChanged(Element.MapElements, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_init = false;
}
}
else if (changed)
{
if (NativeMap != null)
{
UpdateVisibleRegion(NativeMap.CameraPosition.Target);
}
if (Element.MoveToLastRegionOnLayoutChange)
MoveToRegion(Element.LastMoveToRegion, false);
2016-03-22 23:02:25 +03:00
}
}
2018-05-15 18:16:52 +03:00
protected virtual void OnMapReady(GoogleMap map)
{
if (map == null)
{
return;
}
2018-05-15 18:16:52 +03:00
map.SetOnCameraMoveListener(this);
map.MarkerClick += OnMarkerClick;
map.InfoWindowClick += OnInfoWindowClick;
map.MapClick += OnMapClick;
2018-05-15 18:16:52 +03:00
map.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled;
map.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled;
map.UiSettings.ScrollGesturesEnabled = Map.HasScrollEnabled;
SetUserVisible();
SetMapType();
}
2018-05-15 18:16:52 +03:00
protected virtual MarkerOptions CreateMarker(Pin pin)
{
var opts = new MarkerOptions();
opts.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
opts.SetTitle(pin.Label);
opts.SetSnippet(pin.Address);
2018-05-15 18:16:52 +03:00
return opts;
}
2016-03-22 23:02:25 +03:00
void AddPins(IList pins)
2016-03-22 23:02:25 +03:00
{
GoogleMap map = NativeMap;
2016-03-22 23:02:25 +03:00
if (map == null)
{
2016-03-22 23:02:25 +03:00
return;
}
2016-03-22 23:02:25 +03:00
if (_markers == null)
{
_markers = new List<Marker>();
}
2016-03-22 23:02:25 +03:00
_markers.AddRange(pins.Cast<Pin>().Select(p =>
{
Pin pin = p;
var opts = CreateMarker(pin);
var marker = map.AddMarker(opts);
2016-03-22 23:02:25 +03:00
pin.PropertyChanged += PinOnPropertyChanged;
2016-03-22 23:02:25 +03:00
// associate pin with marker for later lookup in event handlers
2019-03-27 21:38:38 +03:00
pin.MarkerId = marker.Id;
2016-03-22 23:02:25 +03:00
return marker;
}));
}
void PinOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Pin pin = (Pin)sender;
Marker marker = GetMarkerForPin(pin);
if (marker == null)
{
return;
}
if (e.PropertyName == Pin.LabelProperty.PropertyName)
{
marker.Title = pin.Label;
}
else if (e.PropertyName == Pin.AddressProperty.PropertyName)
{
marker.Snippet = pin.Address;
}
else if (e.PropertyName == Pin.PositionProperty.PropertyName)
{
marker.Position = new LatLng(pin.Position.Latitude, pin.Position.Longitude);
}
}
protected Marker GetMarkerForPin(Pin pin)
{
Marker targetMarker = null;
if (_markers != null)
{
for (int i = 0; i < _markers.Count; i++)
{
var marker = _markers[i];
if (marker.Id == (string)pin.MarkerId)
{
targetMarker = marker;
break;
}
}
}
return targetMarker;
}
protected Pin GetPinForMarker(Marker marker)
2016-03-22 23:02:25 +03:00
{
Pin targetPin = null;
for (int i = 0; i < Map.Pins.Count; i++)
{
var pin = Map.Pins[i];
if ((string)pin.MarkerId == marker.Id)
{
targetPin = pin;
break;
}
}
return targetPin;
}
void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
{
var pin = GetPinForMarker(e.Marker);
if (pin == null)
{
return;
}
// Setting e.Handled = true will prevent the info window from being presented
// SendMarkerClick() returns the value of PinClickedEventArgs.HideInfoWindow
bool handled = pin.SendMarkerClick();
e.Handled = handled;
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var marker = e.Marker;
var pin = GetPinForMarker(marker);
2016-03-22 23:02:25 +03:00
if (pin == null)
{
return;
2016-03-22 23:02:25 +03:00
}
#pragma warning disable CS0618
pin.SendTap();
#pragma warning restore CS0618
// SendInfoWindowClick() returns the value of PinClickedEventArgs.HideInfoWindow
bool hideInfoWindow = pin.SendInfoWindowClick();
if (hideInfoWindow)
{
marker.HideInfoWindow();
}
2016-03-22 23:02:25 +03:00
}
void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
{
Map.SendMapClicked(new Position(e.Point.Latitude, e.Point.Longitude));
}
void MoveToRegion(MapSpan span, bool animate)
2016-03-22 23:02:25 +03:00
{
GoogleMap map = NativeMap;
if (map == null)
{
return;
}
span = span.ClampLatitude(85, -85);
var ne = new LatLng(span.Center.Latitude + span.LatitudeDegrees / 2,
span.Center.Longitude + span.LongitudeDegrees / 2);
var sw = new LatLng(span.Center.Latitude - span.LatitudeDegrees / 2,
span.Center.Longitude - span.LongitudeDegrees / 2);
CameraUpdate update = CameraUpdateFactory.NewLatLngBounds(new LatLngBounds(sw, ne), 0);
try
{
if (animate)
{
map.AnimateCamera(update);
}
else
{
map.MoveCamera(update);
2016-03-22 23:02:25 +03:00
}
}
catch (IllegalStateException exc)
{
System.Diagnostics.Debug.WriteLine("MoveToRegion exception: " + exc);
Log.Warning("Xamarin.Forms MapRenderer", $"MoveToRegion exception: {exc}");
}
}
2016-03-22 23:02:25 +03:00
void OnPinCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
switch (notifyCollectionChangedEventArgs.Action)
{
case NotifyCollectionChangedAction.Add:
AddPins(notifyCollectionChangedEventArgs.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemovePins(notifyCollectionChangedEventArgs.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemovePins(notifyCollectionChangedEventArgs.OldItems);
AddPins(notifyCollectionChangedEventArgs.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
if (_markers != null)
{
for (int i = 0; i < _markers.Count; i++)
_markers[i].Remove();
_markers = null;
}
AddPins((IList)Element.Pins);
break;
case NotifyCollectionChangedAction.Move:
//do nothing
break;
}
}
void OnMoveToRegionMessage(Map s, MapSpan a)
{
MoveToRegion(a, true);
}
void RemovePins(IList pins)
{
GoogleMap map = NativeMap;
if (map == null)
{
return;
}
if (_markers == null)
{
return;
}
foreach (Pin p in pins)
{
p.PropertyChanged -= PinOnPropertyChanged;
var marker = GetMarkerForPin(p);
if (marker == null)
{
continue;
}
marker.Remove();
_markers.Remove(marker);
}
}
void SetMapType()
{
GoogleMap map = NativeMap;
if (map == null)
{
return;
2016-03-22 23:02:25 +03:00
}
switch (Map.MapType)
{
case MapType.Street:
map.MapType = GoogleMap.MapTypeNormal;
break;
case MapType.Satellite:
map.MapType = GoogleMap.MapTypeSatellite;
break;
case MapType.Hybrid:
map.MapType = GoogleMap.MapTypeHybrid;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
void UpdateVisibleRegion(LatLng pos)
{
GoogleMap map = NativeMap;
if (map == null)
{
return;
}
Projection projection = map.Projection;
int width = Control.Width;
int height = Control.Height;
LatLng ul = projection.FromScreenLocation(new global::Android.Graphics.Point(0, 0));
LatLng ur = projection.FromScreenLocation(new global::Android.Graphics.Point(width, 0));
LatLng ll = projection.FromScreenLocation(new global::Android.Graphics.Point(0, height));
LatLng lr = projection.FromScreenLocation(new global::Android.Graphics.Point(width, height));
double dlat = Math.Max(Math.Abs(ul.Latitude - lr.Latitude), Math.Abs(ur.Latitude - ll.Latitude));
double dlong = Math.Max(Math.Abs(ul.Longitude - lr.Longitude), Math.Abs(ur.Longitude - ll.Longitude));
Element.SetVisibleRegion(new MapSpan(new Position(pos.Latitude, pos.Longitude), dlat, dlong));
2016-03-22 23:02:25 +03:00
}
void MapElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (sender)
{
case Polyline polyline:
{
PolylineOnPropertyChanged(polyline, e);
break;
}
case Polygon polygon:
{
PolygonOnPropertyChanged(polygon, e);
break;
}
case Circle circle:
{
CircleOnPropertyChanged(circle, e);
break;
}
}
}
void OnMapElementCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddMapElements(e.NewItems.Cast<MapElement>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveMapElements(e.OldItems.Cast<MapElement>());
break;
case NotifyCollectionChangedAction.Replace:
RemoveMapElements(e.OldItems.Cast<MapElement>());
AddMapElements(e.NewItems.Cast<MapElement>());
break;
case NotifyCollectionChangedAction.Reset:
if (_polylines != null)
{
for(int i = 0; i < _polylines.Count; i++)
_polylines[i].Remove();
_polylines = null;
}
if (_polygons != null)
{
for (int i = 0; i < _polygons.Count; i++)
_polygons[i].Remove();
_polygons = null;
}
if (_circles != null)
{
for (int i = 0; i < _circles.Count; i++)
_circles[i].Remove();
_circles = null;
}
AddMapElements(Element.MapElements);
break;
}
}
void AddMapElements(IEnumerable<MapElement> mapElements)
{
foreach (var element in mapElements)
{
element.PropertyChanged += MapElementPropertyChanged;
switch (element)
{
case Polyline polyline:
AddPolyline(polyline);
break;
case Polygon polygon:
AddPolygon(polygon);
break;
case Circle circle:
AddCircle(circle);
break;
}
}
}
void RemoveMapElements(IEnumerable<MapElement> mapElements)
{
foreach (var element in mapElements)
{
element.PropertyChanged -= MapElementPropertyChanged;
switch (element)
{
case Polyline polyline:
RemovePolyline(polyline);
break;
case Polygon polygon:
RemovePolygon(polygon);
break;
case Circle circle:
RemoveCircle(circle);
break;
}
}
}
#region Polylines
protected virtual PolylineOptions CreatePolylineOptions(Polyline polyline)
{
var opts = new PolylineOptions();
opts.InvokeColor(polyline.StrokeColor.ToAndroid(Color.Black));
opts.InvokeWidth(polyline.StrokeWidth);
foreach (var position in polyline.Geopath)
{
opts.Points.Add(new LatLng(position.Latitude, position.Longitude));
}
return opts;
}
protected APolyline GetNativePolyline(Polyline polyline)
{
APolyline targetPolyline = null;
if (_polylines != null)
{
for (int i = 0; i < _polylines.Count; i++)
{
var native = _polylines[i];
if (native.Id == (string)polyline.MapElementId)
{
targetPolyline = native;
break;
}
}
}
return targetPolyline;
}
protected Polyline GetFormsPolyline(APolyline polyline)
{
Polyline targetPolyline = null;
for (int i = 0; i < Map.MapElements.Count; i++)
{
var element = Map.MapElements[i];
if ((string)element.MapElementId == polyline.Id)
{
targetPolyline = element as Polyline;
break;
}
}
return targetPolyline;
}
void PolylineOnPropertyChanged(Polyline formsPolyline, PropertyChangedEventArgs e)
{
var nativePolyline = GetNativePolyline(formsPolyline);
if (nativePolyline == null)
{
return;
}
if (e.PropertyName == MapElement.StrokeColorProperty.PropertyName)
{
nativePolyline.Color = formsPolyline.StrokeColor.ToAndroid(Color.Black);
}
else if (e.PropertyName == MapElement.StrokeWidthProperty.PropertyName)
{
nativePolyline.Width = formsPolyline.StrokeWidth;
}
else if (e.PropertyName == nameof(Polyline.Geopath))
{
nativePolyline.Points = formsPolyline.Geopath.Select(position => new LatLng(position.Latitude, position.Longitude)).ToList();
}
}
void AddPolyline(Polyline polyline)
{
var map = NativeMap;
if (map == null)
{
return;
}
if (_polylines == null)
{
_polylines = new List<APolyline>();
}
var options = CreatePolylineOptions(polyline);
var nativePolyline = map.AddPolyline(options);
polyline.MapElementId = nativePolyline.Id;
_polylines.Add(nativePolyline);
}
void RemovePolyline(Polyline polyline)
{
var native = GetNativePolyline(polyline);
if (native != null)
{
native.Remove();
_polylines.Remove(native);
}
}
#endregion
#region Polygons
protected virtual PolygonOptions CreatePolygonOptions(Polygon polygon)
{
var opts = new PolygonOptions();
opts.InvokeStrokeColor(polygon.StrokeColor.ToAndroid(Color.Black));
opts.InvokeStrokeWidth(polygon.StrokeWidth);
if (!polygon.StrokeColor.IsDefault)
opts.InvokeFillColor(polygon.FillColor.ToAndroid());
// Will throw an exception when added to the map if Points is empty
if (polygon.Geopath.Count == 0)
{
opts.Points.Add(new LatLng(0, 0));
}
else
{
foreach (var position in polygon.Geopath)
{
opts.Points.Add(new LatLng(position.Latitude, position.Longitude));
}
}
return opts;
}
protected APolygon GetNativePolygon(Polygon polygon)
{
APolygon targetPolygon = null;
if (_polygons != null)
{
for (int i = 0; i < _polygons.Count; i++)
{
var native = _polygons[i];
if (native.Id == (string)polygon.MapElementId)
{
targetPolygon = native;
break;
}
}
}
return targetPolygon;
}
protected Polygon GetFormsPolygon(APolygon polygon)
{
Polygon targetPolygon = null;
for (int i = 0; i < Element.MapElements.Count; i++)
{
var element = Element.MapElements[i];
if ((string)element.MapElementId == polygon.Id)
{
targetPolygon = (Polygon)element;
break;
}
}
return targetPolygon;
}
void PolygonOnPropertyChanged(Polygon polygon, PropertyChangedEventArgs e)
{
var nativePolygon = GetNativePolygon(polygon);
if (nativePolygon == null)
return;
if (e.PropertyName == MapElement.StrokeColorProperty.PropertyName)
{
nativePolygon.StrokeColor = polygon.StrokeColor.ToAndroid(Color.Black);
}
else if (e.PropertyName == MapElement.StrokeWidthProperty.PropertyName)
{
nativePolygon.StrokeWidth = polygon.StrokeWidth;
}
else if (e.PropertyName == Polygon.FillColorProperty.PropertyName)
{
nativePolygon.FillColor = polygon.FillColor.ToAndroid();
}
else if (e.PropertyName == nameof(polygon.Geopath))
{
nativePolygon.Points = polygon.Geopath.Select(p => new LatLng(p.Latitude, p.Longitude)).ToList();
}
}
void AddPolygon(Polygon polygon)
{
var map = NativeMap;
if (map == null)
{
return;
}
if (_polygons == null)
{
_polygons = new List<APolygon>();
}
var options = CreatePolygonOptions(polygon);
var nativePolygon = map.AddPolygon(options);
polygon.MapElementId = nativePolygon.Id;
_polygons.Add(nativePolygon);
}
void RemovePolygon(Polygon polygon)
{
var native = GetNativePolygon(polygon);
if (native != null)
{
native.Remove();
_polygons.Remove(native);
}
}
#endregion
#region Circles
protected virtual CircleOptions CreateCircleOptions(Circle circle)
{
var opts = new CircleOptions()
.InvokeCenter(new LatLng(circle.Center.Latitude, circle.Center.Longitude))
.InvokeRadius(circle.Radius.Meters)
.InvokeStrokeWidth(circle.StrokeWidth);
if (!circle.StrokeColor.IsDefault)
opts.InvokeStrokeColor(circle.StrokeColor.ToAndroid());
if (!circle.FillColor.IsDefault)
opts.InvokeFillColor(circle.FillColor.ToAndroid());
return opts;
}
protected ACircle GetNativeCircle(Circle circle)
{
ACircle targetCircle = null;
if (_circles != null)
{
for (int i = 0; i < _circles.Count; i++)
{
var native = _circles[i];
if (native.Id == (string)circle.MapElementId)
{
targetCircle = native;
break;
}
}
}
return targetCircle;
}
protected Circle GetFormsCircle(ACircle circle)
{
Circle targetCircle = null;
for (int i = 0; i < Element.MapElements.Count; i++)
{
var mapElement = Element.MapElements[i];
if ((string)mapElement.MapElementId == circle.Id)
{
targetCircle = mapElement as Circle;
break;
}
}
return targetCircle;
}
void CircleOnPropertyChanged(Circle formsCircle, PropertyChangedEventArgs e)
{
var nativeCircle = GetNativeCircle(formsCircle);
if (nativeCircle == null)
{
return;
}
if (e.PropertyName == Circle.FillColorProperty.PropertyName)
{
nativeCircle.FillColor = formsCircle.FillColor.ToAndroid();
}
else if (e.PropertyName == Circle.CenterProperty.PropertyName)
{
nativeCircle.Center = new LatLng(formsCircle.Center.Latitude, formsCircle.Center.Longitude);
}
else if (e.PropertyName == Circle.RadiusProperty.PropertyName)
{
nativeCircle.Radius = formsCircle.Radius.Meters;
}
else if (e.PropertyName == MapElement.StrokeColorProperty.PropertyName)
{
nativeCircle.StrokeColor = formsCircle.StrokeColor.ToAndroid();
}
else if(e.PropertyName == MapElement.StrokeWidthProperty.PropertyName)
{
nativeCircle.StrokeWidth = formsCircle.StrokeWidth;
}
}
void AddCircle(Circle circle)
{
var map = NativeMap;
if (map == null)
{
return;
}
if (_circles == null)
{
_circles = new List<ACircle>();
}
var options = CreateCircleOptions(circle);
var nativeCircle = map.AddCircle(options);
circle.MapElementId = nativeCircle.Id;
_circles.Add(nativeCircle);
}
void RemoveCircle(Circle circle)
{
var native = GetNativeCircle(circle);
if (native != null)
{
native.Remove();
_circles.Remove(native);
}
}
#endregion
void SetUserVisible()
{
GoogleMap map = NativeMap;
if (map == null)
{
return;
}
if (Map.IsShowingUser)
{
var coarseLocationPermission = ContextCompat.CheckSelfPermission(Context, Manifest.Permission.AccessCoarseLocation);
var fineLocationPermission = ContextCompat.CheckSelfPermission(Context, Manifest.Permission.AccessFineLocation);
if (coarseLocationPermission == Permission.Granted || fineLocationPermission == Permission.Granted)
{
map.MyLocationEnabled = map.UiSettings.MyLocationButtonEnabled = true;
}
else
{
Log.Warning("Xamarin.Forms.MapRenderer", "Missing location permissions for IsShowingUser");
map.MyLocationEnabled = map.UiSettings.MyLocationButtonEnabled = false;
}
}
else
{
map.MyLocationEnabled = map.UiSettings.MyLocationButtonEnabled = false;
}
}
void IOnMapReadyCallback.OnMapReady(GoogleMap map)
{
NativeMap = map;
OnMapReady(map);
}
void GoogleMap.IOnCameraMoveListener.OnCameraMove()
{
UpdateVisibleRegion(NativeMap.CameraPosition.Target);
}
2016-03-22 23:02:25 +03:00
}
}