Xamarin Forms Maps 将图像动态添加到 InfoWindow

问题描述:

我已经从官方网站的示例中实现了自定义渲染器,但我确实需要每个引脚发送不同的图像.图像将通过 API 作为 base64 字符串.我真的需要为 android 和 iOS 实现.

I've implemented custom renderers from an example from the official site, but I really need for each pin to send different image. Image would come through API as base64 string. I really need implementation for android and iOS.

案例场景:我正在将 CustomPins 加载到地图上.CustomPins 具有不同的字段,例如:Height、Width、NumberOfParticipants、Image1、Image2

case scenario: Im loading CustomPins to the map. CustomPins have different fileds such as: Height, Width, NumberOfParticipants, Image1, Image2

例如我现在拥有的:

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

        public CustomMapRenderer(Context context) : base(context)
        {
        }

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

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

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

        protected override void OnMapReady(GoogleMap map)
        {
            base.OnMapReady(map);

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

        protected override MarkerOptions CreateMarker(Pin pin)
        {
            var marker = new MarkerOptions();
            marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
            marker.SetTitle(pin.Label);
            marker.SetSnippet(pin.Address);
            marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
            return marker;
        }

        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.Name.Equals("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.Position == position)
                {
                    return pin;
                }
            }
            return null;
        }
    }
}

所以,现在,我从我的布局文件夹(xml 结构)中展示我的 InfoWindow:

So, now, Im presenting my InfoWindow from my layout folder (xml strucutre):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/xamarin" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/monkey" />
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_margin="10dp">
            <TextView
                android:id="@+id/InfoWindowTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="InfoWindowTitle"
                android:textColor="@android:color/black"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/InfoWindowSubtitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="InfoWindowSubtitle"
                android:textColor="@android:color/black" />
        </LinearLayout>
        <ImageButton
            android:id="@+id/InfoWindowButton"
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:src="@drawable/info" />
    </LinearLayout>
</LinearLayout>

那么,有没有办法在运行时将资源图像更改为其他来源?或者我需要做一些不同的事情?

So, is there a way that in a runtime I changed resource image to some other source? Or I need something different to do?

这是一个android的例子,我也需要一个iOS的渲染器例子

This is an example for android, also I need renderer example for iOS too

更新:有没有办法动态生成布局?因为我会有不同类型的引脚 - 每种类型都有不同的 InfoWindow

UPDATE: Is there a way to generate layout also dynamically? Because I would have different types of pins - each type would be having a different InfoWindow

从共享代码中,您还可以在运行时中的 GetInfoContents 方法中更改资源图像.

From shared code ,you also can change the resource image in GetInfoContents method in a runtime .

var formsMap;
...
formsMap = (CustomMap)e.NewElement;
...  

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.Name.Equals("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);
        // init imageButton here
        var imageButton =  view.FindViewById<ImageButton>(Resource.Id.InfoWindowButton);

        if (infoTitle != null)
        {
            infoTitle.Text = marker.Title;
        }
        if (infoSubtitle != null)
        {
            infoSubtitle.Text = marker.Snippet;
        }
        if (imageButton != null)
        {
            var resourcePath = formsMap.ImageSourcePath;
            if (resourcePath == "1"){
               imageButton.SetImageResource(Resource.Drawable.IconOne);
            }else if (resourcePath == "1"){
               imageButton.SetImageResource(Resource.Drawable.IconTwo);
            }
            // or use url image source 
            var imageBitmap = GetImageBitmapFromUrl(resourcePath);
            imageButton.SetImageBitmap(imageBitmap);
        }

        return view;
    }
    return null;
}

那需要CustomMap声明一个ImageSourcePath属性,不管是要加载Resources Image还是url image,他们都可以做到这一点.

That needs CustomMap to declare a ImageSourcePath property , no matter want to load Resources Image or url image , they all can achieve that .

public class CustomMap : Map
{
    ...
    
    public static readonly BindableProperty ImageSourcePathProperty = BindableProperty.Create("ImageSourcePath", typeof(string), typeof(CustomMap), null);

    public string ImageSourcePath
    {
        get { return (string)GetValue(ImageSourcePathProperty); }
        set { SetValue(ImageSourcePathProperty, value); }
    }

    ...
}