Xamarin.forms Xamarin形成自定义地图Pin

Xamarin.forms Xamarin形成自定义地图Pin,xamarin.forms,maps,customization,Xamarin.forms,Maps,Customization,在我正在开发的一个应用程序中,我需要使用自定义的地图别针,我遵循了Xamarin的指南,并借用了他们的示例代码来尝试制作我自己的示例 它在某种程度上可以使“信息”窗口实际更新为自定义布局,但地图图钉从未更改 我的CustomMapRenderer: using System; using System.Collections.Generic; using System.ComponentModel; using Android.Content; using Android.Gms.Maps;

在我正在开发的一个应用程序中,我需要使用自定义的地图别针,我遵循了Xamarin的指南,并借用了他们的示例代码来尝试制作我自己的示例

它在某种程度上可以使“信息”窗口实际更新为自定义布局,但地图图钉从未更改

我的CustomMapRenderer:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using WorkingWithMaps.Droid.Renderers;
using WorkingWithMaps;

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



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

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

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

        void IOnMapReadyCallback.OnMapReady(GoogleMap googleMap)
        {

            map = googleMap;
            map.SetInfoWindowAdapter(this);
            map.InfoWindowClick += OnInfoWindowClick;
            this.NativeMap = googleMap;
        }


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

            if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
            {
                map.Clear();

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

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

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

            if (changed)
            {
                isDrawn = false;
            }
        }

        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 Android.Views.LayoutInflater;
            if (inflater != null)
            {
                Android.Views.View view;

                var customPin = GetCustomPin(marker);
                if (customPin == null)
                {
                    throw new Exception("Custom pin not found");
                }

                if (customPin.Id == "Xamarin")
                {
                    view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
                }
                else
                {
                    view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
                }

                var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
                var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);

                if (infoTitle != null)
                {
                    infoTitle.Text = marker.Title;
                }
                if (infoSubtitle != null)
                {
                    infoSubtitle.Text = marker.Snippet;
                }

                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;
        }
    }
}
抛出异常


Github存储库:

首先,我想提供两个链接,帮助我理解这个问题。谢谢你们。 及

最新的Xamarin地图打破了一个元素属性随VisibleRegion的变化。他们定义MapRenderer现在实现了IOnMapReadyCallback,并以某种方式破坏了OnElementPropertyChanged(我没有研究如何以及为什么)。正如你们在我提供的链接中看到的,有两种方法你们可以选择。保持渲染器实现IOnMapReadyCallback与否。当我保留IOnMapReadyCallback时,我开始获得两个pin——一个是最上面的一个——我们的自定义pin和常规pin。我没有进一步挖掘这是如何发生的,并删除了IOnMapReadyCallback。之后事情变得非常简单,因为如果让Xamarin处理并创建NativeMap,您可以删除一些代码并使渲染器更简单

在我发布代码之前,我还想提一下,当我修复它时,应用程序开始崩溃,出现OutOfMemory异常,我发现你的pin图像宽度为2000像素。我把它改成了40。代码如下:

public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter//, IOnMapReadyCallback
{
    bool isDrawn;

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

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

    }


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

        if (!isMapReady && (NativeMap != null))
        {
            NativeMap.SetInfoWindowAdapter(this);
            NativeMap.InfoWindowClick += OnInfoWindowClick;
            isMapReady = true;
        }

        if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
        {
            NativeMap.Clear();

            foreach (var pin in ((CustomMap)Element).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;
        }
    }

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

        if (changed)
        {
            isDrawn = false;
        }
    }

    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 Android.Views.LayoutInflater;
        if (inflater != null)
        {
            Android.Views.View view;

            var customPin = GetCustomPin(marker);
            if (customPin == null)
            {
                throw new Exception("Custom pin not found");
            }

            if (customPin.Id == "Xamarin")
            {
                view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
            }
            else
            {
                view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
            }

            var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
            var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);

            if (infoTitle != null)
            {
                infoTitle.Text = marker.Title;
            }
            if (infoSubtitle != null)
            {
                infoSubtitle.Text = marker.Snippet;
            }

            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 ((CustomMap)Element).CustomPins)
        {
            if (pin.Pin.Position == position)
            {
                return pin;
            }
        }
        return null;
    }


}
public类CustomMapRenderer:MapRenderer,GoogleMap.IInfoWindowAdapter//,IOnMapReadyCallback
{
布尔是拉文;
受保护的覆盖无效OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs e)
{
基础。一个要素发生变化(e);
if(e.OldElement!=null)
{
NativeMap.InfoWindowClick-=OnInfoWindowClick;
}
}
布尔·伊斯马普雷迪;
受保护的覆盖无效OnElementPropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(发送方,e);
如果(!isMapReady&(NativeMap!=null))
{
NativeMap.SetInfoWindowAdapter(此);
NativeMap.InfoWindowClick+=OnInfoWindowClick;
isMapReady=true;
}
if(e.PropertyName.Equals(“VisibleRegion”)&&&!isDrawn)
{
NativeMap.Clear();
foreach(var pin in((CustomMap)元素).CustomPins)
{
var marker=新的MarkerOptions();
marker.SetPosition(新板条(针.针.位置.纬度,针.针.位置.经度));
marker.SetTitle(pin.pin.Label);
标记器固定接头(引脚地址);
SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn=true;
}
}
受保护覆盖仅限无效布局(布尔值已更改、整数l、整数t、整数r、整数b)
{
仅基础布局(已更改,l、t、r、b);
如果(更改)
{
isDrawn=假;
}
}
无效OnInfoWindowClick(对象发送者,GoogleMap.InfoWindowClickEventArgs e)
{
var customPin=GetCustomPin(即标记);
如果(customPin==null)
{
抛出新异常(“未找到自定义pin”);
}
如果(!string.IsNullOrWhiteSpace(customPin.Url))
{
var url=Android.Net.Uri.Parse(customPin.url);
var intent=新的intent(intent.ActionView,url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(标记器)
{
var inflater=Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService)作为Android.Views.LayoutInflater;
如果(充气机!=null)
{
Android.Views.View;
var customPin=GetCustomPin(标记);
如果(customPin==null)
{
抛出新异常(“未找到自定义pin”);
}
如果(customPin.Id==“Xamarin”)
{
视图=充气机.Inflate(Resource.Layout.xamarinMapInfo窗口,null);
}
其他的
{
视图=充气机.Inflate(Resource.Layout.MapInfo窗口,null);
}
var infoTitle=view.findviewbyd(Resource.Id.InfoWindowTitle);
var infoSubtitle=view.findviewbyd(Resource.Id.InfoWindowSubtitle);
if(infoTitle!=null)
{
infoTitle.Text=marker.Title;
}
if(infoSubtitle!=null)
{
infoSubtitle.Text=marker.Snippet;
}
返回视图;
}
返回null;
}
public Android.Views.View GetInfoWindow(标记器)
{
返回null;
}
CustomPin GetCustomPin(标记注释)
{
变量位置=新位置(annotation.position.Latitude,annotation.position.Longitude);
foreach(var pin in((CustomMap)元素).CustomPins)
{
if(pin.pin.Position==位置)
{
回位销;
}
}
返回null;
}
}

首先,我想提供两个帮助我理解问题的链接。谢谢你们。 及

最新的Xamarin地图打破了一个元素属性随VisibleRegion的变化。他们定义MapRenderer现在实现了IOnMapReadyCallback,并以某种方式破坏了OnElementPropertyChanged(我没有研究如何以及为什么)。正如你们在我提供的链接中看到的,有两种方法你们可以选择。保持渲染器实现IOnMapReadyCallback与否。当我保留IOnMapReadyCallback时,我开始获得两个pin——一个是最上面的一个——我们的自定义pin和常规pin。我没有进一步挖掘这是如何发生的,并删除了IOnMapReadyCallback。在那之后事情变得非常简单因为如果你让Xamarin来处理
var currentpos = await maplocator.GetPositionAsync(1000);
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter//, IOnMapReadyCallback
{
    bool isDrawn;

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

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

    }


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

        if (!isMapReady && (NativeMap != null))
        {
            NativeMap.SetInfoWindowAdapter(this);
            NativeMap.InfoWindowClick += OnInfoWindowClick;
            isMapReady = true;
        }

        if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
        {
            NativeMap.Clear();

            foreach (var pin in ((CustomMap)Element).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;
        }
    }

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

        if (changed)
        {
            isDrawn = false;
        }
    }

    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 Android.Views.LayoutInflater;
        if (inflater != null)
        {
            Android.Views.View view;

            var customPin = GetCustomPin(marker);
            if (customPin == null)
            {
                throw new Exception("Custom pin not found");
            }

            if (customPin.Id == "Xamarin")
            {
                view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
            }
            else
            {
                view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
            }

            var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
            var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);

            if (infoTitle != null)
            {
                infoTitle.Text = marker.Title;
            }
            if (infoSubtitle != null)
            {
                infoSubtitle.Text = marker.Snippet;
            }

            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 ((CustomMap)Element).CustomPins)
        {
            if (pin.Pin.Position == position)
            {
                return pin;
            }
        }
        return null;
    }


}