[This is part of the Android Diary.] In particular, drawing and interacting on them. Herewith a very specialized set of notes that might be of interest to anyone programming to Android’s very attractive map API. I’ve learned a lot already, but I’ll try to keep this up-to-date as I become more conversant with the state of the art.

[Ed. Note: I’ve been asked why on earth I’m putting all this time into a non-Sun platform. The answer: I’m now officially in our new Cloud Computing group. The connection between Mobile and Cloud seems painfully obvious.]

Three Big Pieces · Just like it says in all the online write-ups. You need a MapActivity and inside that a MapView and on top of that an Overlay , and the framework takes care of their interactions with each other very nicely.

Do It in XML · I ended up with a bunch of bits and pieces of UI: the map itself, the map’s standard zoom control, and the popup that appears when you click on an entry from a geotagged feed.

It turns out that you can specify them all in XML files in the res/layout directory. If they’re not always visible, who cares? The zoomer takes care of its own visibility, and you can switch your own bits&pieces in and out of sight with view.setVisibility , which the framework handles very intelligently. Here’s my XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView android:id="@+id/mapper" android:layout_width="fill_parent" android:layout_height="fill_parent" android:enabled="true" android:clickable="true" android:apiKey="...secret elided..." /> <LinearLayout android:id="@+id/zoomer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> <RelativeLayout android:id="@+id/entrypopup" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="10px" android:visibility="gone" android:background="#80000000"> <TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Entry Popup..." android:textColor="#ffffffff" android:textSize="18.5sp" /> <Button android:id="@+id/buttonVisit" android:text="Visit it" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/title" android:layout_alignParentRight="true" android:layout_marginLeft="10px" /> <Button android:id="@+id/buttonCancel" android:text="Don’t visit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/title" android:layout_toLeftOf="@id/buttonVisit" android:layout_marginLeft="10px" /> </RelativeLayout> </RelativeLayout>

Overlays Are Your Friends · If you want to decorate a map and interact with the decorations, ignore all the controlling and eventing calls on MapActivity and MapView , and go straight to Overlay .

You put all your map decorations in overlay.draw and the framework takes care of the rest. Be careful though: your overlay.draw routine is going to get called an insanely huge number of times, so think about being as lazy as possible inside it.

I wasted an immense amount of time dealing with various kind of “Touch” events at various levels before I realized that overlay.onTap just Does The Right Thing and furthermore takes care of the translation between map and screen co-ordinates.

I suspect it will be helpful to show the core of my event-handling logic:

@Override public boolean onTap(GeoPoint point, MapView view) { checkTolerance(view); // entry here? Entry e = finder.find(point.getLatitudeE6(), point.getLongitudeE6()); if (e != null) { // OK, they picked an entry; show them the popup. visitor.setEntry(e); popup.setVisibility(View.VISIBLE); TextView text = (TextView) container.findViewById(R.id.title); text.setText(e.title()); } return super.onTap(point, view); }

Pretty straightforward, eh? The only thing that’s even remotely subtle is the checkTolerance call, which computes how “close” a touch to the location of an entry is considered to have hit it. Since the bookkeeping is in geographical rather than screen units, this changes every time you zoom or unzoom.

They say you’re supposed to return true if you’ve “handled” the event, but doing so led to some odd and hard-to-reproduce breakage, letting the superclass have its chance never did.

The Zoomer · You have to do a little bit of extra work if you want people to zoom into and out of your programmatically-displayed maps. I cribbed the answer from StackOverflow; hey, the first time I’ve linked there, I wonder if it’s a trend.