Alex Sorokoletov Notes     About     Archive     Feed

Android-maps-utils bindings for Xamarin.Android apps

Bindings for android-maps-utils for Xamarin.Android apps

Many developers use Google Maps for Android (Google Play Services) to display maps and related content in Xamarin-based Android apps.

Javascript SDK for Google Maps has clustering and other neat stuff built in. Android Maps SDK has none of this included. However, there is a solution!

To address this problem, Google Maps team shared so called android-maps-utils extensions to the Android maps SDK which allows you do following:

  • Marker clustering — handles the display of a large number of points
  • Heat maps — display a large number of points as a heat map
  • IconGenerator — display text on your Markers (see screenshot to the right)
  • Poly decoding and encoding — compact encoding for paths, interoperability with Maps API web services
  • Spherical geometry — for example: computeDistance, computeHeading, computeArea
  • KML — displays KML data (Caution: Beta!)
  • GeoJSON — displays and styles GeoJSON data

(Please read more on the official website: http://googlemaps.github.io/android-maps-utils/)

Down the road you can find steps and details how the bindings for quite complex java library could be created and how these fresh bindings for Android Maps utils could be consumed in the Xamarin Android application.

To get to the how to part, scroll down

Existing Xamarin.Android bindings

Particularly I was interesting about clustering, as the application has to display around 500 markers in a dense area.

Looking for existing solution to the problem, one can find bindings made by Øystein Heimark. But these are quite old. To practice and to get the freshest code, I’ve created the updated bindings:

github.com/alexsorokoletov/Xamarin.Android.Maps.Utils

Creating java library bindings for Xamarin.Android

There is an official guide from Xamarin: Binding a Java Library - Consuming Java libraries from C#

Following the guide, one can sometimes see warnings or erros on the bindings. For example, /GoogleMapsUtilityBinding/obj/Debug/generated/src/Com.Google.Maps.Android.Geojson.GeoJsonLineStringStyle.cs(118,118): Error CS0234: The type or namespace name 'IGeoJsonStyle' does not exist in the namespace 'Com.Google.Maps.Android.Geojson'. Are you missing an assembly reference? (CS0234) (GoogleMapsUtilityBinding)

To fix this error all you need is to manually edit Metadata.xml file and introduce these changes

As you proceed, there will be a few more errors and several warnings. Fixes for them are documented in the Metadata.xml file

More than helpful were comments and examples from brendanzagaeski with different approaches to different java-to-csharp binding problems. Links to these examples are in the end of the page.

JAR/AAR dependencies

Case with android-maps-utils is interesting because the jar/aar file depends on google-play-services.jar and android-support-v4.jar. Xamarin suggests to add these jar files as ReferenceJar items by setting Build Action to ReferenceJar. What is missing here for a successful compilation is a C# side of these reference jars. After adding the jars you need to add a Nuget packages or Xamarin components with C# bindings for these jars. With that change,C# bindings generator will generate correct code an generated C# code will be compilable, because you already have C# part of the referenced libraries.

One of the questions one might have - which version of google-play-services.jar should we use? The android-maps-utils library is compiled against google-play-services 7.8.0, so, basically, we can use anything >=7.8.0

How to use this library (examples)

On C# side what you need to do is following: 0) reference the bindings 1) instantiate cluster manager

//on MapReady
_clusterManager = new ClusterManager(this, _map);
_map.SetOnCameraChangeListener(_clusterManager);
_map.SetOnMarkerClickListener(_clusterManager);

If you already have custom handlers to map CameraChange/MarkerClick, you can do following (specifics of Xamarin.Android events subscriptions)

        
private void map_MarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
{
    var handled = _clusterManager.OnMarkerClick(e.Marker);
    if (handled)
        return;
    //your custom code
}
private void map_CameraChange(object sender, GoogleMap.CameraChangeEventArgs e)
{
    _clusterManager.OnCameraChange(e.Position);
    //your custom code
}
		

2) implement custom IClusterItem object

public class ServiceClusterItem : Java.Lang.Object, IClusterItem
{
    public ServiceClusterItem(ServiceItem serviceItem)
    {
        Position = new LatLng(serviceItem.Lat, serviceItem.Lon);
    }

    public ServiceClusterItem(IntPtr handle, Android.Runtime.JniHandleOwnership transfer)
        : base(handle, transfer)
    {
        
    }

    public LatLng Position
    {
        get;
        set;
    }
}

3) add items to the clusterer and enjoy

var clusterItem = new ServiceClusterItem(...);
_clusterManager.AddItem(clusterItem);

What if you have another version of Google Play Services already linked to your main project? In that case fork the repo and remove references that it has currently and then add references to the same packages and versions you have in your main project.


If you liked this post, you can share it with your followers or follow me on Twitter!