Revision as of 19:25, 22 May 2012 by jasfox (Talk | contribs)

Creating Touchable Custom Map Components for the Maps API for Java ME

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to create a framework for custom MapComponents which respond to touch Events. A series of touchable components (scale bar, button, attribution image) are then added using the framework.

Touch component java me.png



In order to maximize the screen real estate available to display a Map, it is necessary to invoke Full Screen mode. This can be done when the MapCanvas is initialised, through a call to MapCanvas.setFullScreenMode(true). Command buttons cannot be used when a full screen is displayed, and therefore it is necessary to add at least one MapComponent to allow the user to interact with the Map. This article proposes a framework for creating touchable MapComponents and a series of compoments such as a custon button and a scale bar are added to an example MIDlet


The job of the TouchableDisplayComponent can be conveniently split into three parts.

  • The component must display something (usually an Image) when added to the Map
  • The component may require a background to be displayed behind the Image to avoid the Image bleeding into to the display.
  • The component, a subclass or a nominated delegate needs to be able to handle touch events.

Displaying an Image

Displaying an Image is easy, it is merely a matter of overriding the default paint() event and calling Graphics.drawImage(), further code can be taken from the How to create a marker tooltip with Maps API for Java ME example to add a rounded box with a border around it. An example paint() method is shown below, where constants such as borderColor. borderWidth and backgroundColor are set in the constructor.

    public TouchableDisplayComponent(Image bitmap, int anchor, int border, int borderColor,
int backgroundColor) {
this.bitmap = bitmap;
this.border = border;
this.borderColor = borderColor;
this.backgroundColor = backgroundColor;
this.anchor = anchor;
public void paint(Graphics g) {
if (border > 0) {
g.fillRoundRect(getBorderAnchor().getX(), getBorderAnchor().getY(),
getImageWidth() + (getBorder() * 2), getImageHeight() + (getBorder() * 2),
if (backgroundColor > NO_FILL) {
// Draw the background.
g.fillRoundRect(getBackgroundAnchor().getX(), getBackgroundAnchor().getY(),
if (bitmap != null) {
getBackgroundAnchor().getX(), getBackgroundAnchor().getY(),
Graphics.TOP | Graphics.LEFT);

The location of the bitmap image to display on the MapDisplay can only be defined once the component has been attached to amap. This must be calculated in the form of x and y in pixels.For ease of use the TouchableDisplayComponent can be attached to any corner of a MapDisplay since it uses the enumerated anchor locations defined in the Graphics class

 public void attach(MapDisplay map) {
this.map = map;
protected void calculateBorders(MapDisplay map) {
int x = getOffset().getX();
int y = getOffset().getY();
if ((getAnchor() & Graphics.BOTTOM) != 0) {
y = map.getHeight() - getImageHeight() - getOffset().getY();
if ((getAnchor() & Graphics.RIGHT) != 0) {
x = map.getWidth() - getImageWidth() - getOffset().getX();
setBorderAnchor(new Point(x - getBorder(),
y - getBorder()));
setBackgroundAnchor(new Point(x, y));

Creating an EventHandler

The TouchableDisplayComponent contains a private class to handle touch events (all other events are ignored), and a TouchEventListener to delegate events through a known interface. This TouchEventListener is added through the usual system of getters and setters. The pointerPressed() method of the private class checks to see if a touch has occurred within the target area, and if it has, calls thedoClick() method

        public boolean pointerPressed(int x, int y) {
if (x > getBackgroundAnchor().getX()
&& x < getBackgroundAnchor().getX() + getImageWidth()
&& y > getBackgroundAnchor().getY()
&& y < getBackgroundAnchor().getY() + getImageHeight()) {
this.component.doClick(x - getBackgroundAnchor().getX(), y
+ getBackgroundAnchor().getY());
return true;
return false;

The default implementation of thedoClick() method searches for a TouchEventListener and passes thread of control to the listener if one exists.

    public boolean doClick(int x, int y) {
if (listener != null) {
listener.touchAction(this, x, y);
return true;
return false;

Example TouchableDisplayComponents

Using the base class described above it is possible to create a variety of touchable components


The TouchTextComponent extends the TouchableDisplayComponent class to display text rather than an image. This is achieved by passing in a null image in the constructor and overriding paint(). Once the border and background have been painted, the text can be added as shown. Note : the width and height of the background are now based on the length of the text. The call to paint() is conditional on text being present.

public class TouchTextComponent extends TouchableDisplayComponent {
protected int getImageHeight() {
return font.getHeight() + 4;
protected int getImageWidth() {
return font.stringWidth(text) + (2 * TEXT_MARGIN);
public void paint(Graphics g) {
if ("".equals(getText()) == false) {
getText(), getBackgroundAnchor().getX() + TEXT_MARGIN, getBackgroundAnchor().getY(), Graphics.TOP | Graphics.LEFT);


It is also possible to conditionally display an image, through overriding paint(). This example is for use with overlays, and will only display if an overlay is present.

public class AttributionMapComponent extends TouchableDisplayComponent {
public void paint(Graphics g) {
if (getMap() != null && getMap().getAllMapOverlays().length > 0) {


The following {Icode|TouchableDisplayComponent}} creates a scale bar which is sized according to the zoom level. Since the Map uses the normalized Mercator projection, the length of the scale bar needs to be altered depending on the latitudes displayed on the map. Clicking on the scale bar will switch between Metric and Imperial Measurements. This component is self contained, and doesn't need to delegate its events. It overrides doClick() and handles it in its entrity.

public class ScaleBarComponent extends TouchTextComponent {
private int y_coord;
private int x_coord_start;
private int x_coord_end;
private boolean imperial;
final static int[] SCALE_IN_METRES = new int[]{5000000, 2000000, 1000000,
600000, 300000, 150000, 75000, 30000, 20000, 10000, 5000, 2000, 1000,
500, 250, 100, 50, 25};
final static String[] SCALE_IN_METRES_TEXT = new String[]{"5000km", "2000km", "1000km",
"600km", "300km", "150km", "75km", "30km", "20km", "10km", "5km", "2km", "1km",
"500m", "250m", "100m", "50m", "25m"};
final static int[] SCALE_IN_IMPERIAL = new int[]{4828000, 2414000, 1207000, 643737, 321868, 160934, 80467, 40233, 24140,
16093, 8046, 3218, 1609, 457, 228, 91, 45, 22};
final static String[] SCALE_IN_IMPERIAL_TEXT = new String[]{"3000 miles", "1500 miles", "750 miles",
"400 miles", "200 miles", "100 miles", "50 miles", "25 miles", "15 miles", "10 miles", "5 miles", "2 miles", "1 mile",
"500 yds", "250 yds", "100 yds", "50 yds", "75 ft"};
... etc
public void mapUpdated(boolean zoomChanged) {
x_coord_end = getBackgroundAnchor().getX() + getImageWidth() - TEXT_MARGIN;
y_coord = getBackgroundAnchor().getY() + getImageHeight() - TEXT_MARGIN;
double zoom = getMap().getZoomLevel();
zoom = (zoom >= getMap().getMinZoomLevel()) ? zoom : getMap().getMinZoomLevel();
if (isImperial()) {
} else {
private void displayMetric(double zoom) {
if (zoom <= SCALE_IN_METRES.length - 1) {
GeoCoordinate end = getMap().pixelToGeo(new Point(x_coord_end, y_coord));
x_coord_start = x_coord_end - 10;
while (getMap().pixelToGeo(new Point(x_coord_start, y_coord)).distanceTo(end) < SCALE_IN_METRES[(int) zoom]) {
setText(SCALE_IN_METRES_TEXT[(int) zoom]);
private void displayImperial(double zoom) {
if (zoom <= SCALE_IN_IMPERIAL.length - 1) {
GeoCoordinate end = getMap().pixelToGeo(new Point(x_coord_end, y_coord));
x_coord_start = x_coord_end - 10;
while (getMap().pixelToGeo(new Point(x_coord_start, y_coord)).distanceTo(end) < SCALE_IN_IMPERIAL[(int) zoom]) {
setText(SCALE_IN_IMPERIAL_TEXT[(int) zoom]);
public void paint(Graphics g) {
if ("".equals(getText()) == false) {
g.drawLine(x_coord_start, y_coord, x_coord_end, y_coord);
g.drawLine(x_coord_start, y_coord + 1, x_coord_end, y_coord + 1);
g.drawLine(x_coord_start, y_coord + 3, x_coord_start, y_coord);
g.drawLine(x_coord_end, y_coord + 3, x_coord_end, y_coord);
public boolean doClick(int x, int y) {
return true;
public boolean isImperial() {
return imperial;
public void setImperial(boolean imperial) {
this.imperial = imperial;
Article Metadata
Code ExampleTested with
Devices(s): X3-02
Device(s): All
Dependencies: Maps API for Java ME v1.0
Keywords: Location API, Ovi Maps, MapComponent, MapDisplay, FullScreen, Scale bar, Button, Touch
Created: jasfox (29 May 2012)
Updated: :
Last edited: jasfox (22 May 2012)


Add categories below. Remove Category:Draft when the page is complete or near complete

227 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.


Thank you!

We appreciate your feedback.