Xamarin Forms 自定义地图图钉

问题描述 投票:0回答:2

在我正在开发的应用程序之一中,我需要使用自定义地图图钉,并且我已按照 Xamarin 上的指南进行操作 https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom -renderer/map/customized-pin/ 以及借用了他们的示例代码来尝试制作我自己的示例。

它在一定程度上起作用,信息窗口实际上更新为自定义布局,但地图图钉永远不会改变。

[Imgur

我的自定义地图渲染器:

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

还有我的地图页面,也大量借用了 Xamarin 的地图使用指南

using Plugin.Geolocator;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Xaml;

namespace WorkingWithMaps
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MainPage : ContentPage
    {
        CustomMap map;
        Geocoder geoCoder;
        String navAdd;

        public MainPage()
        {
            InitializeComponent();

            var maplocator = CrossGeolocator.Current;
            maplocator.DesiredAccuracy = 1;
            geoCoder = new Geocoder();

            map = new CustomMap
            {
                HeightRequest = 100,
                WidthRequest = 960,
                VerticalOptions = LayoutOptions.FillAndExpand,
                IsShowingUser = true
            };

            map.MapType = MapType.Street;
            map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(55.237208, 10.479160), Distance.FromMeters(500)));
            map.IsShowingUser = true;

            var street = new Button { Text = "Street" };
            var hybrid = new Button { Text = "Hybrid" };
            var satellite = new Button { Text = "Satellite" };
            street.Clicked += HandleClickedAsync;
            hybrid.Clicked += HandleClickedAsync;
            //satellite.Clicked += OnReverseGeocodeButtonClicked;
            var segments = new StackLayout
            {
                Spacing = 30,
                HorizontalOptions = LayoutOptions.CenterAndExpand,
                Orientation = StackOrientation.Horizontal,
                Children = { street, hybrid, satellite }
            };

            Content = new StackLayout
            {
                HorizontalOptions = LayoutOptions.Center,
                Children = { map, segments }
            };

            Device.BeginInvokeOnMainThread(async () =>
            {
                try
                {

                    //var currentpos = await maplocator.GetPositionAsync(1000);
                    //map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(currentpos.Latitude, currentpos.Longitude), Distance.FromMeters(500)));

                    if (!maplocator.IsListening)
                    {
                        await maplocator.StartListeningAsync(1000, 50, true);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Fail" + ex);
                }
            });

            var pin = new CustomPin
            {
                Pin = new Pin
                {
                    Type = PinType.Place,
                    Position = new Position(55.240121, 10.469895),
                    Label = "Testing Pins"
                }
            };

            map.CustomPins = new List<CustomPin> { pin };
            map.Pins.Add(pin.Pin);

            map.PropertyChanged += (sender, e) =>
            {
                Debug.WriteLine(e.PropertyName + " just changed!");
                if (e.PropertyName == "VisibleRegion" && map.VisibleRegion != null)
                    CalculateBoundingCoordinates(map.VisibleRegion);
            };

            maplocator.PositionChanged += (sender, e) =>
            {
                var position = e.Position;

                map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(position.Latitude, position.Longitude), Distance.FromKilometers(2)));
            };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //async void OnReverseGeocodeButtonClicked(object sender, EventArgs e)
        //{
        //    var possibleAddresses = await geoCoder.GetAddressesForPositionAsync(pin.Position);
        //    navAdd += possibleAddresses.ElementAt(0) + "\n";

        //    switch (Device.OS)
        //    {
        //        case TargetPlatform.iOS:
        //            Device.OpenUri(new Uri(string.Format("http://maps.apple.com/?q={0}", WebUtility.UrlEncode(navAdd))));
        //            break;
        //        case TargetPlatform.Android:
        //            Device.OpenUri(new Uri(string.Format("geo:0,0?q={0}", WebUtility.UrlEncode(navAdd))));
        //            break;
        //        case TargetPlatform.Windows:
        //        case TargetPlatform.WinPhone:
        //            Device.OpenUri(new Uri(string.Format("bingmaps:?where={0}", Uri.EscapeDataString(navAdd))));
        //            break;
        //    }
        //}


        void HandleClickedAsync(object sender, EventArgs e)
        {
            var b = sender as Button;
            switch (b.Text)
            {
                case "Street":
                    map.MapType = MapType.Street;
                    break;
                case "Hybrid":
                    map.MapType = MapType.Hybrid;
                    break;
                case "Satellite":
                    map.MapType = MapType.Satellite;
                    break;
            }
        }

        static void CalculateBoundingCoordinates(MapSpan region)
        {
            var center = region.Center;
            var halfheightDegrees = region.LatitudeDegrees / 2;
            var halfwidthDegrees = region.LongitudeDegrees / 2;

            var left = center.Longitude - halfwidthDegrees;
            var right = center.Longitude + halfwidthDegrees;
            var top = center.Latitude + halfheightDegrees;
            var bottom = center.Latitude - halfheightDegrees;

            if (left < -180) left = 180 + (180 + left);
            if (right > 180) right = (right - 180) - 180;

            Debug.WriteLine("Bounding box:");
            Debug.WriteLine("                    " + top);
            Debug.WriteLine("  " + left + "                " + right);
            Debug.WriteLine("                    " + bottom);
        }

    }
}

除了上述问题之外,实施还导致

IsShowingUser = True
不再发挥作用

var currentpos = await maplocator.GetPositionAsync(1000);

抛出异常。

Github 存储库:https://github.com/Mortp/CustomMapPinsXamarin

xamarin.forms maps customization
2个回答
3
投票

首先我想提供两个帮助我理解问题的链接。感谢你们。 Xamarin.Forms.Maps 2.3.4 自定义 MapRenderer 禁用所有功能https://forums.xamarin.com/discussion/92565/android-ionmapreadycallback-forms-2-3-4

最新的 Xamarin Maps 打破了 OnElementPropertyChanged 与 VisibleRegion 的关系。他们定义 MapRenderer 现在实现了 IOnMapReadyCallback 并且以某种方式破坏了 OnElementPropertyChanged (我没有调查如何以及为什么)。正如您在我提供的链接中看到的,您可以采用两种方法。保持渲染器实现 IOnMapReadyCallback 或不实现。当我保留 IOnMapReadyCallback 时,我开始获得 2 个引脚 - 一个位于顶部,另一个 - 我们的自定义引脚和常规引脚。我没有进一步挖掘这是如何发生的,并删除了 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;
    }


}


0
投票

使用该命令可以使用map.onCreate(mapViewBundle);这是一个消息:Google Map 不涉及 map.onCreate(mapViewBundle); 的定义。 Alguem consegue me ajudar?

© www.soinside.com 2019 - 2024. All rights reserved.