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

398 строки
9.3 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.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.OS;
using Java.Lang;
using Xamarin.Forms.Internals;
2016-03-22 23:02:25 +03:00
using Xamarin.Forms.Platform.Android;
using Math = System.Math;
namespace Xamarin.Forms.Maps.Android
{
public class MapRenderer : ViewRenderer<Map, MapView>,
GoogleMap.IOnCameraChangeListener, 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;
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 void OnCameraChange(CameraPosition pos)
{
UpdateVisibleRegion(pos.Target);
}
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 -= OnCollectionChanged;
}
2016-03-22 23:02:25 +03:00
if (NativeMap != null)
{
NativeMap.MyLocationEnabled = false;
NativeMap.SetOnCameraChangeListener(null);
NativeMap.InfoWindowClick -= MapOnMarkerClick;
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 -= OnCollectionChanged;
MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
2016-03-22 23:02:25 +03:00
if (NativeMap != null)
{
NativeMap.SetOnCameraChangeListener(null);
2016-03-22 23:02:25 +03:00
NativeMap.InfoWindowClick -= MapOnMarkerClick;
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
var incc = Map.Pins as INotifyCollectionChanged;
if (incc != null)
{
2016-03-22 23:02:25 +03:00
incc.CollectionChanged += OnCollectionChanged;
}
}
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)
{
2016-03-22 23:02:25 +03:00
gmap.MyLocationEnabled = gmap.UiSettings.MyLocationButtonEnabled = Map.IsShowingUser;
}
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);
OnCollectionChanged(Element.Pins, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_init = false;
}
}
else if (changed)
{
if (NativeMap != null)
{
UpdateVisibleRegion(NativeMap.CameraPosition.Target);
}
MoveToRegion(Element.LastMoveToRegion, false);
2016-03-22 23:02:25 +03:00
}
}
protected virtual void OnMapReady(GoogleMap map)
{
if (map == null)
{
return;
}
map.SetOnCameraChangeListener(this);
map.InfoWindowClick += MapOnMarkerClick;
map.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled;
map.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled;
map.UiSettings.ScrollGesturesEnabled = Map.HasScrollEnabled;
map.MyLocationEnabled = map.UiSettings.MyLocationButtonEnabled = Map.IsShowingUser;
SetMapType();
}
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);
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
// associate pin with marker for later lookup in event handlers
pin.Id = marker.Id;
return marker;
}));
}
void MapOnMarkerClick(object sender, GoogleMap.InfoWindowClickEventArgs eventArgs)
2016-03-22 23:02:25 +03:00
{
// clicked marker
var marker = eventArgs.Marker;
// lookup pin
Pin targetPin = null;
for (var i = 0; i < Map.Pins.Count; i++)
{
Pin pin = Map.Pins[i];
2016-03-22 23:02:25 +03:00
if ((string)pin.Id != marker.Id)
{
2016-03-22 23:02:25 +03:00
continue;
}
2016-03-22 23:02:25 +03:00
targetPin = pin;
break;
}
// only consider event handled if a handler is present.
// Else allow default behavior of displaying an info window.
targetPin?.SendTap();
2016-03-22 23:02:25 +03:00
}
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 OnCollectionChanged(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:
_markers?.ForEach(m => m.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)
{
var marker = _markers.FirstOrDefault(m => (object)m.Id == p.Id);
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 IOnMapReadyCallback.OnMapReady(GoogleMap map)
{
NativeMap = map;
OnMapReady(map);
}
2016-03-22 23:02:25 +03:00
}
}