What happened with Google maps on Forms?

RicardoSRicardoS ESMember ✭✭

It used to load properly, but now i stays on 0,0 instead of zooming on where y want and does not display the pins or polyline.
What is going on!?

Best Answer

  • NMackayNMackay GBInsider, University ✭✭✭✭✭
    Accepted Answer

    @RicardoS

    The OnElementPropertyChanged approach doesn't work anymore.

    Here's my renderer code, my hack to fix the zoom isn't very pretty but I haven't got round to updating it yet.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Android.Gms.Maps;
    using Android.Gms.Maps.Model;
    using Foobar.Common.CustomRenderers;
    using Foobar.Common.Resources.Images;
    using Foobar.Droid.Renderers;
    using Xamarin.Forms;
    using Xamarin.Forms.Maps;
    using Xamarin.Forms.Maps.Android;
    using Xamarin.Forms.Platform.Android;
    
    [assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
    namespace Foobar.Droid.Renderers
    {
        public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
        {
            private bool _disposed = false;
            private GoogleMap _map;
            private CustomMap _formsMap;
    
    
            protected override void Dispose(bool disposing)
            {
                if (_disposed)
                    return;
    
                _map.MarkerClick -= HandleMarkerClick;
                _formsMap = null;
                _map = null;
                _disposed = true;
    
                base.Dispose(disposing);
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement != null)
                {
                    _formsMap = (CustomMap)e.NewElement;
                    ((MapView)Control).GetMapAsync(this);
                }
            }
    
    
            public void OnMapReady(GoogleMap googleMap)
            {
                if (_map != null) return;
    
                _map = googleMap;
    
                _map.Clear();
                _map.MarkerClick += HandleMarkerClick;
                _map.MyLocationEnabled = _formsMap.IsShowingUser;
    
                foreach (var formsPin in _formsMap.CustomPins)
                {
    
                    var markerWithIcon = new MarkerOptions();
                    markerWithIcon.SetPosition(new LatLng(formsPin.Pin.Position.Latitude,
                        formsPin.Pin.Position.Longitude));
                    markerWithIcon.SetTitle(formsPin.Pin.Label);
                    markerWithIcon.SetSnippet(formsPin.Pin.Address);
    
                    if (formsPin.Angle > 0)
                    {
                        markerWithIcon.SetRotation((float) formsPin.Angle);
                    }
                    markerWithIcon.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.mapCustomPin));
    
    
                    var m = _map.AddMarker(markerWithIcon);
    
                    if (formsPin.RouteCoordinates.Any())
                    {
                        var polylineOptions = new PolylineOptions();
                        polylineOptions.InvokeColor(0x66FF0000);
    
                        foreach (var coordinate in formsPin.RouteCoordinates)
                        {
                            polylineOptions.Add(new LatLng(coordinate.Latitude, coordinate.Longitude));
                        }
    
                        _map.AddPolyline(polylineOptions);
    
                    }
                    if (formsPin.ShowCallout)
                        m.ShowInfoWindow();
    
                    // Temp hack because maptype and mapspan are been ignored
                    switch (_formsMap.MapType)
                    {
                        case MapType.Street:
                            _map.MapType = GoogleMap.MapTypeTerrain;
                            break;
                        case MapType.Satellite:
                            _map.MapType = GoogleMap.MapTypeSatellite;
                            break;
                        case MapType.Hybrid:
                            _map.MapType = GoogleMap.MapTypeHybrid;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
    
                    if (_formsMap.CustomPins.Count == 1)
                    {
                        var firstOrDefault = _formsMap.CustomPins.FirstOrDefault();
                        if (firstOrDefault == null) continue;
                        var pos = firstOrDefault.Pin.Position;
    
                        CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
                        builder.Target(new LatLng(pos.Latitude, pos.Longitude));
                        builder.Zoom(4);
                        var cameraPosition = builder.Build();
                        var cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
    
                        _map.MoveCamera(cameraUpdate);
                    }
                    // end of temp change
                }
            }
    
            private void HandleMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
            {
                e.Handled = true;
                var marker = e.Marker;
    
                if (marker.IsInfoWindowShown) // Always returns false, check
                    marker.HideInfoWindow();
                else
                    marker.ShowInfoWindow();
            }
        }
    }
    

Answers

  • RicardoSRicardoS ESMember ✭✭
    edited May 15

    Update: Now it displays the pins, but no polylines (routes between pins) nor zooming (getting close to the center of the route). All of these functions work on iOS, so I assumed the responsible was the Android Custom Renderer.

    Here you have said code:

    `using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using Android.Gms.Maps;
    using GmapsCross;
    using GmapsCross.Droid;
    using Xamarin.Forms;
    using Xamarin.Forms.Maps;
    using Xamarin.Forms.Maps.Android;

    [assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
    namespace GmapsCross.Droid {
    public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback {
    List routeCoordinates;
    List customPins;
    GoogleMap map;
    bool isDrawn;

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e) {
            base.OnElementChanged(e);
            if (e.OldElement != null) {
                // Unsubscribe
            }
            if (e.NewElement != null) {
                CustomMap customMap = (CustomMap)e.NewElement;
                customPins = customMap.CustomPins;
                routeCoordinates = customMap.RouteCoordinates;
                ((MapView)Control).GetMapAsync(this);
                //Control.GetMapAsync (this);
                if (customMap != null) {
                    ((ObservableCollection<Pin>)customMap.Pins).CollectionChanged += OnCollectionChanged;
                }
            }
        }
    
        public void OnMapReady(GoogleMap googleMap) {
            do {
                map = googleMap;
            } while (googleMap == null);
        }
    
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals("VisibleRegion") && !isDrawn) {
                UpdatePins();
            }
        }
    
        void OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
            UpdatePins();
        }
    
        void UpdatePins() {
            //if (Element != null) {
                //CustomMap customMap = (CustomMap)Element;
                if (map != null) {
                    map.Clear();
                    //customPins = customMap.CustomPins;
                    foreach (CustomPin customPin in customPins) {
                        map.AddMarker(Graficos.newMarker(customPin));
                    }
                    isDrawn = true;
                    map.AddPolyline (Graficos.newPolyline (routeCoordinates));
                } else {
                    Console.WriteLine("Fallo map");
                }
            /*} else {
                Console.WriteLine("Fallo custom");
            }*/
        }
    }
    

    }`

  • RicardoSRicardoS ESMember ✭✭
    edited May 15

    Sorry, I meant 'No polylines nor zooming'
    Edited previous comment

  • RicardoSRicardoS ESMember ✭✭
    edited May 15

    More updates:

    I generated an APK and installed it in a device, drawing the polyline route but still cannot zoom in the center I give to the map (from the corresponding xaml.cs page). And sometimes, when you click on a pin, it crashes.

    Even more updates:

    The above happens now in debug mode too.

  • RicardoSRicardoS ESMember ✭✭

    Here you have the zooming feature I use on the xaml.cs Page:

    mapView.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(39.518871, -0.435062), Distance.FromMiles(3.0)));

    in case it should be different now...

  • VarunBabuSVarunBabuS INMember ✭✭✭

    hi @RicardoS For drawing markers and Polylines could you please replace your UpdatePins function with below code

    void UpdatePins() {
    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);

                        map.AddMarker(marker);
                    }
    
                    for (int i = 0; i < routeCoordinates.Count; i++)
                    {
                        var polylineOptions = new PolylineOptions();
                        polylineOptions.InvokeColor(0x66f43548);
    
                        for (int j = 0; j < routeCoordinates[i].Count; j++)
                        {
                            polylineOptions.Add(new LatLng(double.Parse(routeCoordinates[i][j].latitude, CultureInfo.InvariantCulture), double.Parse(routeCoordinates[i][j].longitude, CultureInfo.InvariantCulture)));
                        }
                        map.AddPolyline(polylineOptions);
                    }
    
                    isDrawn = true;
    

    }

    Regarding Graficos I am sorry because of no clarity about it Please let me know if possible what the same is...
    Please try the above code and try to share your feedback if both markers and polylines are rendered..

  • RicardoSRicardoS ESMember ✭✭

    Sorry, I forget sometimes that Graficos exists and I kinda take for granted what that code does.
    Here you go:

    `using System.Collections.Generic;
    using Android.Gms.Maps.Model;
    using Android.Graphics;
    using Xamarin.Forms.Maps;

    namespace GmapsCross.Droid {
    public static class Graficos {

        public static MarkerOptions newMarker(CustomPin customPin) {
            MarkerOptions marker = new MarkerOptions();
            marker.SetPosition(new LatLng(customPin.Pin.Position.Latitude, customPin.Pin.Position.Longitude));
            marker.SetTitle(customPin.Pin.Label);
            marker.SetSnippet(customPin.Pin.Address);
            int mark = marker.Title.Equals("Bus") ? Resource.Drawable.bus2 : Resource.Drawable.marker_gmaps;
            marker.SetIcon(BitmapDescriptorFactory.FromResource(mark));
            return marker;
        }
    
        public static PolylineOptions newPolyline(List<Position> routeCoordinates) {
            PolylineOptions polylineOptions = new PolylineOptions();
            polylineOptions.InvokeColor((new Color(0, 0, 0, 178)).ToArgb());
            polylineOptions.InvokeWidth(3f);
            foreach (Position position in routeCoordinates) {
                polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
            }
            return polylineOptions;
        }
    }
    

    }`

    Long story short, this class is a builder for the markers and polylines, so your code is an extended version of this snippet...

  • VarunBabuSVarunBabuS INMember ✭✭✭

    hi @RicardoS sorry for delay in response regarding polyline color can you try passing some integer value like

    polylineOptions.InvokeColor(0x66f43548);

    instead of

    polylineOptions.InvokeColor((new Color(0, 0, 0, 178)).ToArgb());

    I remember while trying to do a small R&D on Polyline colors almost 6 months ago or more than that I had noticed following link http://stackoverflow.com/questions/721324/using-toargb-followed-by-fromargb-does-not-result-in-the-original-color
    which may be helpful for you as well

  • RicardoSRicardoS ESMember ✭✭

    Is that the same color as in argh code?
    If yes, I'll consider changing the nomenclature.

    Unfortunately, this does not help with the "zooming" issue.
    Sorry

  • NMackayNMackay GBInsider, University ✭✭✭✭✭

    @RicardoS

    That is a known issue in the map renderer in Forms 2.3.4. There is a workaround on this thread.

    https://forums.xamarin.com/discussion/92565/android-ionmapreadycallback-forms-2-3-4

  • RicardoSRicardoS ESMember ✭✭

    @NMackay Do you mean this? If yes I'll give it a try:

    `
    void InvokeOnMapReadyBaseClassHack(GoogleMap googleMap) {
    System.Reflection.MethodInfo onMapReadyMethodInfo = null;
    Type baseType = typeof(MapRenderer);
    foreach (var currentMethod in baseType.GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly)) {
    if (currentMethod.IsFinal && currentMethod.IsPrivate) {
    if (string.Equals(currentMethod.Name, "OnMapReady", StringComparison.Ordinal)) {
    onMapReadyMethodInfo = currentMethod;
    break;
    }

            if (currentMethod.Name.EndsWith(".OnMapReady", StringComparison.Ordinal)) {
                onMapReadyMethodInfo = currentMethod;
                break;
            }
        }
    }
    
    if (onMapReadyMethodInfo != null) {
        onMapReadyMethodInfo.Invoke(this, new[] { googleMap });
    }
    

    }
    `

    (This code stuff is really convoluted...)

  • RicardoSRicardoS ESMember ✭✭

    The zoom is working, but the Custom pins and Polyline appear as normal pins and none at all respectively...

    So we have taken a few steps back

  • RicardoSRicardoS ESMember ✭✭

    @NMackay
    I forgot the new code:

    `[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
    namespace GmapsCross.Droid {
    public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback {
    List routeCoordinates;
    List customPins;
    GoogleMap map;
    bool isDrawn;

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e) {
            base.OnElementChanged(e);
            if (e.OldElement != null) {
                // Unsubscribe
            }
            if (e.NewElement != null) {
                CustomMap customMap = (CustomMap)e.NewElement;
                customPins = customMap.CustomPins;
                routeCoordinates = customMap.RouteCoordinates;
                ((MapView)Control).GetMapAsync(this);
                //Control.GetMapAsync (this);
                if (customMap != null) {
                    ((ObservableCollection<Pin>)customMap.Pins).CollectionChanged += OnCollectionChanged;
                }
            }
        }
    
        /*public void OnMapReady(GoogleMap googleMap) {
            do {
                map = googleMap;
            } while (googleMap == null);
        }*/
    
        void InvokeOnMapReadyBaseClassHack(GoogleMap googleMap) {
            System.Reflection.MethodInfo onMapReadyMethodInfo = null;
            Type baseType = typeof(MapRenderer);
            foreach (var currentMethod in baseType.GetMethods(
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance |
                System.Reflection.BindingFlags.DeclaredOnly)) {
                if (currentMethod.IsFinal && currentMethod.IsPrivate) {
                    if (string.Equals(currentMethod.Name, "OnMapReady", StringComparison.Ordinal)) {
                        onMapReadyMethodInfo = currentMethod;
                        break;
                    }
    
                    if (currentMethod.Name.EndsWith(".OnMapReady", StringComparison.Ordinal)) {
                        onMapReadyMethodInfo = currentMethod;
                        break;
                    }
                }
            }
    
            if (onMapReadyMethodInfo != null) {
                onMapReadyMethodInfo.Invoke(this, new[] { googleMap });
            }
        }
    
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName.Equals("VisibleRegion") && !isDrawn) {
                UpdatePins();
            }
        }
    
        void OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
            UpdatePins();
        }
    
        void UpdatePins() {
            //if (Element != null) {
            //CustomMap customMap = (CustomMap)Element;
            if (map != null) {
                map.Clear();
                //customPins = customMap.CustomPins;
                foreach (CustomPin customPin in customPins) {
                    map.AddMarker(Graficos.newMarker(customPin));
                }
                isDrawn = true;
                map.AddPolyline(Graficos.newPolyline(routeCoordinates));
            } else {
                Console.WriteLine("Fallo map");
            }
            /*} else {
                Console.WriteLine("Fallo custom");
            }*/
        }
    }
    

    }`

    *Graficos class is in the seventh comment

  • NMackayNMackay GBInsider, University ✭✭✭✭✭
    Accepted Answer

    @RicardoS

    The OnElementPropertyChanged approach doesn't work anymore.

    Here's my renderer code, my hack to fix the zoom isn't very pretty but I haven't got round to updating it yet.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Android.Gms.Maps;
    using Android.Gms.Maps.Model;
    using Foobar.Common.CustomRenderers;
    using Foobar.Common.Resources.Images;
    using Foobar.Droid.Renderers;
    using Xamarin.Forms;
    using Xamarin.Forms.Maps;
    using Xamarin.Forms.Maps.Android;
    using Xamarin.Forms.Platform.Android;
    
    [assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
    namespace Foobar.Droid.Renderers
    {
        public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
        {
            private bool _disposed = false;
            private GoogleMap _map;
            private CustomMap _formsMap;
    
    
            protected override void Dispose(bool disposing)
            {
                if (_disposed)
                    return;
    
                _map.MarkerClick -= HandleMarkerClick;
                _formsMap = null;
                _map = null;
                _disposed = true;
    
                base.Dispose(disposing);
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement != null)
                {
                    _formsMap = (CustomMap)e.NewElement;
                    ((MapView)Control).GetMapAsync(this);
                }
            }
    
    
            public void OnMapReady(GoogleMap googleMap)
            {
                if (_map != null) return;
    
                _map = googleMap;
    
                _map.Clear();
                _map.MarkerClick += HandleMarkerClick;
                _map.MyLocationEnabled = _formsMap.IsShowingUser;
    
                foreach (var formsPin in _formsMap.CustomPins)
                {
    
                    var markerWithIcon = new MarkerOptions();
                    markerWithIcon.SetPosition(new LatLng(formsPin.Pin.Position.Latitude,
                        formsPin.Pin.Position.Longitude));
                    markerWithIcon.SetTitle(formsPin.Pin.Label);
                    markerWithIcon.SetSnippet(formsPin.Pin.Address);
    
                    if (formsPin.Angle > 0)
                    {
                        markerWithIcon.SetRotation((float) formsPin.Angle);
                    }
                    markerWithIcon.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.mapCustomPin));
    
    
                    var m = _map.AddMarker(markerWithIcon);
    
                    if (formsPin.RouteCoordinates.Any())
                    {
                        var polylineOptions = new PolylineOptions();
                        polylineOptions.InvokeColor(0x66FF0000);
    
                        foreach (var coordinate in formsPin.RouteCoordinates)
                        {
                            polylineOptions.Add(new LatLng(coordinate.Latitude, coordinate.Longitude));
                        }
    
                        _map.AddPolyline(polylineOptions);
    
                    }
                    if (formsPin.ShowCallout)
                        m.ShowInfoWindow();
    
                    // Temp hack because maptype and mapspan are been ignored
                    switch (_formsMap.MapType)
                    {
                        case MapType.Street:
                            _map.MapType = GoogleMap.MapTypeTerrain;
                            break;
                        case MapType.Satellite:
                            _map.MapType = GoogleMap.MapTypeSatellite;
                            break;
                        case MapType.Hybrid:
                            _map.MapType = GoogleMap.MapTypeHybrid;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
    
                    if (_formsMap.CustomPins.Count == 1)
                    {
                        var firstOrDefault = _formsMap.CustomPins.FirstOrDefault();
                        if (firstOrDefault == null) continue;
                        var pos = firstOrDefault.Pin.Position;
    
                        CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
                        builder.Target(new LatLng(pos.Latitude, pos.Longitude));
                        builder.Zoom(4);
                        var cameraPosition = builder.Build();
                        var cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
    
                        _map.MoveCamera(cameraUpdate);
                    }
                    // end of temp change
                }
            }
    
            private void HandleMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
            {
                e.Handled = true;
                var marker = e.Marker;
    
                if (marker.IsInfoWindowShown) // Always returns false, check
                    marker.HideInfoWindow();
                else
                    marker.ShowInfoWindow();
            }
        }
    }
    
  • RicardoSRicardoS ESMember ✭✭

    YES!! Finally, it worked!

    Thank you so much for your time and patience, @NMackay !

  • Pamelarios.2249Pamelarios.2249 USUniversity ✭✭

    can you please share the xaml file ?
    thanks

  • RicardoSRicardoS ESMember ✭✭
    edited October 13

    @Pamelarios.2249 Sorry, no xaml file, this is Android

  • Pamelarios.2249Pamelarios.2249 USUniversity ✭✭

    Oh I see ! Thanks

  • RicardoSRicardoS ESMember ✭✭
    edited October 19

    You're welcome, @Pamelarios.2249

Sign In or Register to comment.