×
Namespaces

Variants
Actions
(Difference between revisions)

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

From Nokia Developer Wiki
Jump to: navigation, search
jasfox (Talk | contribs)
(Jasfox - - TouchableDisplayComponent)
jasfox (Talk | contribs)
(Jasfox - - TouchTextComponent)
Line 112: Line 112:
  
 
== TouchTextComponent ==
 
== TouchTextComponent ==
 +
The {{Icode|TouchTextComponent}} extends the {{Icode|TouchableDisplayComponent}} class to display text rather than an image. This is achieved by passing in a null image in the constructor and overriding {{Icode|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
 
<code java>
 
<code java>
public class TouchTextComponent extends TouchableDisplayComponent {
 
 
    protected static final int TEXT_MARGIN = 5;
 
    private String text = "";
 
    private final Font font;
 
    private final int textColor;
 
    private static final String ID = "TextButton";
 
 
    public TouchTextComponent(int anchor,  int border, int borderColor,
 
            int backgroundColor, int textColor, Font font) {
 
        super(null,anchor,  border, borderColor, backgroundColor);
 
        this.font = font;
 
        this.textColor = textColor;
 
    }
 
 
    public TouchTextComponent(int anchor, int border, int borderColor,
 
            int backgroundColor, int textColor) {
 
        this(anchor, border, borderColor, backgroundColor, textColor, Font.getDefaultFont());
 
    }
 
 
    /**
 
    * @return the text
 
    */
 
    public String getText() {
 
        return text;
 
    }
 
 
    /**
 
    * @param text the text to set
 
    */
 
    public void setText(String text) {
 
        this.text = text;
 
    }
 
 
 
     protected int getImageHeight() {
 
     protected int getImageHeight() {
 
         return font.getHeight() + 4;
 
         return font.getHeight() + 4;
Line 155: Line 122:
 
     }
 
     }
  
    public String getId() {
+
         public void paint(Graphics g) {
         return ID;
+
    }
+
 
+
    public void paint(Graphics g) {
+
 
         if ("".equals(getText()) == false) {
 
         if ("".equals(getText()) == false) {
 
             super.paint(g);
 
             super.paint(g);
Line 169: Line 132:
 
}
 
}
 
</code>
 
</code>
 
  
 
== AttributionMapComponent ==
 
== AttributionMapComponent ==

Revision as of 18:53, 22 May 2012


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.


Contents

Introduction

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


TouchableDisplayComponent

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.setColor(borderColor);
g.fillRoundRect(getBorderAnchor().getX(), getBorderAnchor().getY(),
getImageWidth() + (getBorder() * 2), getImageHeight() + (getBorder() * 2),
SMALL_CORNER_ARC, SMALL_CORNER_ARC);
}
 
if (backgroundColor > NO_FILL) {
// Draw the background.
g.setColor(backgroundColor);
g.fillRoundRect(getBackgroundAnchor().getX(), getBackgroundAnchor().getY(),
getImageWidth(),
getImageHeight(), SMALL_CORNER_ARC, SMALL_CORNER_ARC);
}
 
if (bitmap != null) {
g.drawImage(bitmap,
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;
calculateBorders(map);
return;
}
 
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;
}

TouchTextComponent

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

    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) {
super.paint(g);
g.setColor(textColor);
g.drawString(
getText(), getBackgroundAnchor().getX() + TEXT_MARGIN, getBackgroundAnchor().getY(), Graphics.TOP | Graphics.LEFT);
}
}
}

AttributionMapComponent

public class AttributionMapComponent extends TouchableDisplayComponent {
 
private final String id = "attribution";
 
public AttributionMapComponent(Image attribution, int anchor, int border, int borderColor,
int backgroundColor) {
super(attribution, anchor, border, borderColor, backgroundColor);
 
}
 
 
public AttributionMapComponent(Image attribution, int anchor) {
super(attribution, anchor);
}
 
// from MapComponent
public String getId() {
return id;
}
 
public void paint(Graphics g) {
if (getMap() != null && getMap().getAllMapOverlays().length > 0) {
super.paint(g);
}
}
}


ScaleBarComponent

public class ScaleBarComponent extends TouchTextComponent {
 
private static final String ID = "ScaleBar";
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"};
 
public ScaleBarComponent() {
super(Graphics.BOTTOM | Graphics.RIGHT, 0, NO_FILL, NO_FILL, 000000 );
}
 
public ScaleBarComponent(int textColor, Font font) {
super(Graphics.BOTTOM | Graphics.RIGHT, 0, NO_FILL, NO_FILL, textColor, font);
}
 
public ScaleBarComponent(int anchor, int border, int borderColor,
int backgroundColor) {
super(anchor, border, borderColor, backgroundColor, 000000, Font.getDefaultFont());
}
 
public ScaleBarComponent(int anchor, int border, int borderColor,
int backgroundColor, int textColor, Font font) {
super(anchor, border, borderColor, backgroundColor, textColor, font);
 
}
 
 
 
// from MapComponent
public String getId() {
return ID;
}
 
/**
* When the map is updated, we need to check if one of the MapObjects held
* in the Hashtable has the current focus. If so, the tooltip is set,
* the tooltip is cleared otherwise.
* @param zoomChanged
*/

public void mapUpdated(boolean zoomChanged) {
 
 
x_coord_end = getBackgroundAnchor().getX() + getImageWidth() - TEXT_MARGIN;//getMap().getWidth() - X_OFFSET - TEXT_MARGIN;
y_coord = getBackgroundAnchor().getY() + getImageHeight() - TEXT_MARGIN; // getMap().getHeight() - Y_OFFSET - TEXT_MARGIN;
double zoom = getMap().getZoomLevel();
zoom = (zoom >= getMap().getMinZoomLevel()) ? zoom : getMap().getMinZoomLevel();
if (isImperial()) {
displayImperial(zoom);
} else {
displayMetric(zoom);
}
calculateBorders(getMap());
}
 
protected 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]) {
x_coord_start--;
}
 
setText(SCALE_IN_METRES_TEXT[(int) zoom]);
}
}
 
protected 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]) {
x_coord_start--;
}
 
setText(SCALE_IN_IMPERIAL_TEXT[(int) zoom]);
}
}
 
public void paint(Graphics g) {
if ("".equals(getText()) == false) {
super.paint(g);
 
g.setColor(000000);
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);
 
 
/*g.drawString(
getText(), x_coord_end, y_coord, Graphics.BOTTOM | Graphics.RIGHT); */

}
}
 
 
 
public boolean doClick(int x, int y) {
setImperial(!isImperial());
mapUpdated(false);
 
System.out.println("doClick1");
return true;
}
 
/**
* @return the imperial
*/

public boolean isImperial() {
return imperial;
}
 
/**
* @param imperial the imperial to set
*/

public void setImperial(boolean imperial) {
this.imperial = imperial;
}
}
Article Metadata
Code ExampleTested with
Devices(s): X3-02
Compatibility
Device(s): All
Dependencies: Maps API for Java ME v1.0
Article
Keywords: Location API, Ovi Maps, MapComponent, MapDisplay, FullScreen, Scale bar, Button, Touch
Created: jasfox (29 May 2012)
Updated: :
Last edited: jasfox (22 May 2012)



Summary

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

466 page views in the last 30 days.
×