Dhruv Bhagat
Dhruv Bhagat

Reputation: 33

Setting custom marker image would overlay default marker icon along with custom icon

After updating version of forms to 2.4.0.282, I started getting this weird behavior in MapView. I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.

Note that I am using Xamarin.Maps version 2.4.0.282, tried to downgrade to prior versions as well but I am of no help.

I even tried it out by commenting line,

Forms.SetFlags("FastRenderers_Experimental");

but even this didn't help.

Below is the renderer I created,

public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
    {
        GoogleMap map;

        public static double PreviousDistance = 0;

        List<CustomPin> customPins;

        CustomMap formsMap = null;

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                map.InfoWindowClick -= OnInfoWindowClick;
                map.MarkerClick -= OnMarkerClick;
            }

            if (e.NewElement != null)
            {
                formsMap = (CustomMap)e.NewElement;
                customPins = formsMap.CustomPins;
                ((MapView)Control).GetMapAsync(this);
            }
        }

        protected override void OnMapReady(GoogleMap googleMap)
        {
            map = googleMap;
            map.InfoWindowClick += OnInfoWindowClick;

            map.MarkerClick += OnMarkerClick;

            map.UiSettings.ZoomControlsEnabled = false;

            formsMap.MoveToRegion(MapSpan.FromCenterAndRadius(formsMap.Location, Distance.FromMiles(1.0)));

            if(customPins != null && customPins.Count > 0)
            {
                setMapPins("CustomPins");
            }
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == "CustomPins" || (e.PropertyName.Equals("VisibleRegion")))
            {
                setMapPins(e.PropertyName);
            }
        }

        private void setMapPins(string PropertyName)
        {
            customPins = formsMap.CustomPins;

            map.Clear();

            if (customPins != null && customPins.Count > 0)
            {
                if (PropertyName == "CustomPins")
                {
                    //Set map zoom
                    var defaultZoom = 14;

                    try
                    {
                        PreviousDistance = DistanceCalculation.MoveToRegionData.MoveToRegion(formsMap, customPins, defaultZoom, PreviousDistance);
                    }
                    catch (Exception ex)
                    {
                        PreviousDistance = 0;
                        Console.WriteLine(ex.Message);
                    }
                }

                foreach (var pin in customPins)
                {
                    var pinImage = Resources.GetIdentifier(pin.PinImage.ToLower(), "drawable", Context.PackageName);
                    var markerImg = BitmapDescriptorFactory.FromResource(pinImage);

                    map.AddMarker(new MarkerOptions().SetTitle(pin.Pin.Label).SetSnippet(pin.Id).SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude)).SetIcon(markerImg));
                }
            }
            else
            {
                Console.WriteLine("In else!!");
            }
        }

        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            base.OnLayout(changed, l, t, r, b);

            if (changed)
            {
            }
        }

        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 = global::Android.Net.Uri.Parse(customPin.Url);
                var intent = new Intent(Intent.ActionView, url);
                intent.AddFlags(ActivityFlags.NewTask);
                global::Android.App.Application.Context.StartActivity(intent);
            }
        }

        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;
        }

        void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs ea)
        {   
            var marker = (Marker) ea.Marker;
            formsMap.IsPinClicked = false;

            var customPin = GetCustomPin(marker);
            if (customPin == null)
            {
                throw new Exception("Custom pin not found");
            }
            formsMap.SelectedPinId = Convert.ToInt32(marker.Snippet);
            formsMap.IsPinClicked = true;
        }
    }

This is what it looks like right now..

enter image description here

Upvotes: 0

Views: 941

Answers (2)

Pavan V Parekh
Pavan V Parekh

Reputation: 1946

Add NativeMap.Clear(); in OnElementPropertyChanged and try again. Hope it will help!!!

Have a look below code for customizing pin on Android Platform.

[assembly:ExportRenderer (typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.Droid
{
    public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
    {
        List<CustomPin> customPins;
        bool isDrawn;

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                NativeMap.InfoWindowClick -= OnInfoWindowClick;
            }

            if (e.NewElement != null)
            {
                var formsMap = (CustomMap)e.NewElement;
                customPins = formsMap.CustomPins;
                ((MapView)Control).GetMapAsync(this);
            }
        }


        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
            {
                NativeMap.Clear();
                NativeMap.InfoWindowClick += OnInfoWindowClick;
                NativeMap.SetInfoWindowAdapter(this);

                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));

                    NativeMap.AddMarker(marker);
                }
                isDrawn = true;
            }
        }
    }

}

For more detail Click here

Upvotes: 1

Elvis Xia - MSFT
Elvis Xia - MSFT

Reputation: 10841

I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.

I've made a demo and reproduced the problem, after some tests I found that the overriding of OnMapReady function causes the problem. Even with totally empty OnMapReady the problem will occurs. My guess is a call to the OnMapReady in Custom Map Render might lead to a rerender of the Map including pins.

Solution:

  1. Comment OnMapReady out.
  2. Move the logic in OnMapReady to OnElementChanged.
  3. Let your local variable map=NativeMap and make sure setMapPins get called in OnElementPropertyChanged.

Upvotes: 1

Related Questions