Brian Long Consultancy & Training Services
Ltd.
January 2012
We can also implement our own overlays, drawing what we want on the map. Let's make
a custom overlay that draws a circle similar to the one drawn by MyLocationOverlay
.
Also, to make it interesting we'll have it draw the circle centred around our current
detected location and have a radius governed by the current location's accuracy.
Normally when looking at this kind of goal we'd involve the LocationManager
class and check for availability
of network or GPS location providers, set up the LocationListener
interface and add code to
an override of the onLocationChanged()
method, all of which is
discussed in the Obtaining User Location section of the Android SDK Dev Guide. To keep things simple in this
example, however, we'll take advantage of the fact that the MyLocationOverlay
class already does all of that for us. We'll use a MyLocationOverlay
but not have it display the current location or its compass, and instead just use
its own onLocationChanged()
method as the place to
set up the custom overlay on the map.
Before worrying about how we'll shoe-horn our custom overlay into the
MyLocationOverlay
, here's the custom overlay class.
It has a constructor that stores the centre coordinates, radius and colour, and
a draw()
method override to render the circle onto a supplied canvas.
This takes the centre coordinates and turns them into a microdegree-based GeoPoint
,
then uses the map's Projection
to locate the pixel position of
that point on the map. Next the circle radius in metres is converted into a number
of pixels on the map by first identifying how big it would be at the equator and
then using some trigonometry to take into account the curved earth surface and whereabouts the point actually is. After this a transparent filled circle is drawn, followed
by a solid circle to show the circle's edge clearly.
The activity remains pretty much the same as before, although we ignore the compass
and avoid adding the MyLocationOverlay
descendant into the map view's
Overlays
array. So that just leaves the MyLocationOverlay
that we're using to do the heavy lifting with regard to getting location updates:
The constructor gets hold of an Android LocationManager
so that the
onLocationChanged()
method can choose to ignore any location updates
coming from the network provider if it's know that the more accurate GPS provider
is active. If we have already created a custom overlay, this is located in the map's
overlay list and then removed so that we can create a new custom overlay with the
newly identified location and add that instead. On the off-chance that the supplied
location has no accuracy information we use a fallback value of ¼ mile, then
the location, the circle radius and our chosen circle colour are passed into the
custom overlay constructor.
That's about all there is to it. With my GPS unit turned off the custom overlay looks like this after on or two location updates from the network provider:
The final type of overlay we'll look at here is an itemised overlay. This is an
overlay that can display a collection of markers (or overlay items) on the map.
The Google maps library provides a base class for an itemised overlay called
ItemizedOverlay
, which has some abstract methods we must override.
We'll do much the same as the short map tutorial Hello, MapView from the Android SDK Dev Guide. This involves creating a simple
descendant of ItemizedOverlay
that manages the overlay items in an
array list and overrides createItem()
and size()
accordingly. The overlay items (items
of type OverlayItem
) are set up to store a title and
a snippet of text along with a location, all passed into the constructor. You'll
see in the code below that we also override onTap()
so that when one of the markers on
the map (an overlay item) is tapped we display a dialog that uses the title and text snippet.
For a simple itemised overlay that's all that is required: a place to store the overlay items. Supporting user interaction is entirely optional.
The activity that uses the itemised overlay sets up a default marker from a drawable resource in the project. A single overlay item is created that is given a coordinate, along with a title and some text. This overlay item is then added to the itemised overlay, which is added to the map view and the map view is then told to bring the location of the overlay item into view. The key bits of code are here:
Running up the application we now see this:
As you might expect, tapping the marker on the map yields a dialog:
If you are using the RTM release of Oxygene for Java, or a trial version, and are running on JDK 7 then you might conclude that it is not possible to custom-sign your Android application. This is because JDK 7's jarsigner defaults to using an SHA-256 digest algorithm where in fact the digest algorithm we require is SHA1. The Oxygene for Java RTM release doesn't have an option for choosing a digest algorithm. However this needn't be a terminal issue.
You have two options here. One is to simply uninstall JDK 7 and Java 7 and then install JDK 6 from the list of available JDK versions.
The other option is to build your applications using a batch file like
the one below. This allows you to compile your project from the command-line using
MSBuild, which will invoke the Oxygene command-line compiler. The commands
specify that Oxygene should not Java-sign the Android package.
The compilation is then followed by calls to jarsigner and zipalign to complete the build
process, passing the appropriate parameters along. Finally the application is uninstalled from the attached device
(assuming there is an attached device and a previous version of the app was
installed) and the new version is then installed onto the device. If using the
emulator substitute the -d
option with -e
instead.
Note: the batch file assumes that both the JDK bin directory (e.g.
C:\Program Files\Java\jdk1.7.0_02\bin
) and the Android SDK tools directory
(e.g. C:\Android\android-sdk-windows\tools
) are on the Windows PATH
.
The PACKAGE
variable should be given a value matching your Android
package name. This is basically your project name all in lower case, and can be
found as the package
attribute of the manifest
element
in your project's Android manifest file, or alternatively as the Archive name
value on the Application page of the project options. Additionally you should set
up all the details relating to your keystore in the available variables.
This article has looked in some detail at how you set about using Google's
MapView
control in an Android application as built with Oxygene for Java.
It's by no means a simple process and various links and information in regular
Android Java coverage have got out of date, hampering the process of working out
what needs to be done. And then Java 7 came along and made some of the aspects
even more prone to going wrong. Hopefully the attention to detail in this
coverage is sufficient to help clear the muddied waters on the subject and allow
Oxygene for Java developers to readily add mapping capabilities into their
Android applications.
If you wish to make any comments on this article, your best options are to comment on the blog post that announced it, use the Contact Me form or email me at .
Brian Long has spent the last 1.6 decades as a trainer, trouble-shooter and mentor focusing on the Delphi, Oxygene, C# and C++ languages, and the Win32, .NET and Mono platforms, recently adding iOS and Android onto the list. In his spare time, when not exploring the Chiltern Hills on his mountain-bike or pounding the pavement in his running shoes, Brian has been re-discovering and re-enjoying the idiosyncrasies and peccadilloes of Unix-based operating systems. Besides writing a Pascal problem-solving book in the mid-90s he has contributed chapters to several books, written countless magazine articles, spoken at many international developer conferences and acted as occasional Technical Editor for Sybex. Brian has a number of online articles that can be found at http://blong.com and a blog at http://blog.blong.com.
© 2012 Brian Long Consulting and Training Services Ltd. All Rights Reserved.
Go back to the top of this page
Go back to start of this article