Xamarin.Forms.Maps 2.3.4自定义MapRenderer禁用所有内容

时间:2017-04-12 16:28:36

标签: c# google-maps xamarin.forms

我将Xamarin.Forms和Xamarin.Forms.Maps更新到新版本(2.3.4)后出现问题。

之后我还更新了Android项目中的所有google play服务(以及我讨厌的很多库)。

主要问题是我有一个自定义MapRenderer用于自定义引脚,在iOS和UWP中工作正常,但在Android版本中,这个自定义MapRenderer会破坏所有Map。任何属性更改或方法调用似乎都会被忽略。

例如,我有一个按钮来切换地图类型(混合或街道),该操作永远不会改变它。我还注意到(根据本教程:https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/map/customized-pin/)属性“VisibleRegion”永远不会改变,因此以下代码永远不会执行:

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

        if (e.PropertyName.Equals("VisibleRegion") && !_isDrawn)
        {
            // Do something with your custom map
        }
    }

if里面我曾经填充我的自定义引脚(如上面的教程),现在我的地图总是空的。

现在我在IOnMapReadyCallback内填充我的地图,它工作正常,但我仍然有绑定问题。

如果我忽略自定义MapRendered(删除装配线),所有绑定都会正常工作,但我的地图现在有旧引脚,并且所有自定义都已消失(显然)。

在PCL中我有MyMap.MoveToRegion(...)MyMap.MapType = _currentType;之类的内容,但只有在不使用自定义MapRenderer的情况下,这些说明才有效。

我的自定义MapRenderer与上面的教程几乎相同。

使用C#而不是XAML创建自定义Map,它没有任何XAML绑定,但如果我正在使用MapRenderer,则完全忽略任何属性更改或方法调用,如MoveToRegion或MapType。

任何帮助?

由于

2 个答案:

答案 0 :(得分:4)

我已经找到了解决方案。

查看源代码,MapRenderer已经实现了IOnMapReadyCallback,如果在自定义MapRendered中删除实现,一切都会重新开始工作(但没有自定义)。

MapRenderer将Google地图实例保存在属性NativeMap中(也存在属性Map,即Xamarin表单映射实例),因此我们不再需要实现IOnMapReadyCallback 。我认为我们在使用NativeMap时需要小心,因为在开始时它可能是null

在我之前提到的方法中,我这样做:

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

        if (e.PropertyName.Equals("VisibleRegion") && !_isDrawn)
        {
            PopulateMap();
            OnGoogleMapReady();
        }
    }

我在OnMapReady中的代码现在进入OnGoogleMapReady()

    private void OnGoogleMapReady()
    {
        if (_mapReady) return;

        NativeMap.InfoWindowClick += OnInfoWindowClick;
        NativeMap.SetInfoWindowAdapter(this);

        _mapReady = true;
    }

我还在OnElementChanged中添加了此内容,以删除NativeMap

中的所有已注册的委托
        if (e.OldElement != null)
        {
            NativeMap.InfoWindowClick -= OnInfoWindowClick;
        }

目前存在一个Pull Request实现OnMapReady作为虚方法,所以我们可以在我们的实现中覆盖它,现在确定NativeMap不是null,但为此我们需要等待下一个版本。

你可以在这里阅读更多内容 - > https://forums.xamarin.com/discussion/92565/android-ionmapreadycallback-forms-2-3-4

答案 1 :(得分:0)

我遇到了同样的问题,由于Xamarin论坛上的answer,我解决了这个问题。

这是我的地图渲染器(Android部分),用于替换标记的图像:

[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MyNamespace.Droid
{
    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;
                Control.GetMapAsync(this);
            }
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.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.fake_ic_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);
            }
        }

        void IOnMapReadyCallback.OnMapReady(GoogleMap googleMap) 
        { 
            InvokeOnMapReadyBaseClassHack(googleMap); 
            map = googleMap;
            map.SetInfoWindowAdapter(this);
            map.InfoWindowClick += OnInfoWindowClick;
        }

        public Android.Views.View GetInfoContents(Marker marker)
        {
            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;
        }

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