Sag mir wo du stehst – Android MapView

Wie vor kurzem geschrieben, beschäftige ich mich im Moment mit der App-Entwicklung für Android. Es gibt für mich wirklich noch sehr viel zu Lernen und meine Example-App wird immer größer. Immer wieder entdecke ich dabei neue Funktionen und hin und wieder auch Probleme. Erfreulich ist, dass man im Netz zu fast allen Themen Hilfe findet. Leider aber nicht zu allen.
Aktuell wollte ich erreichen, dass der Nutzer einfach per Antippen des Bildschirmes eine Adresse von GoogleMaps auswählen kann und diese angezeigt bekommt.
Klingt einfach oder? Dachte ich mir auch. Aber entweder habe ich etwas nicht verstanden oder ich habe schlichtweg etwas falsch gemacht.

Wie hier gezeigt, habe ich eine MapActivity erstellt, die als Result Longitude und Latitude der gewählten Koordinate zurückgeben sollte.
Meine MapView in der Activity wollte aber einfach die onTouch-Events nicht fangen, die ich per setOnTouchListener behandeln wollte.
Es ist mir beim Debuggen etwas Merkwürdiges aufgefallen: Sobald ich per mapView.setBuiltInZoomControls(true); die Zoom-Buttons angezeigt habe, wurde immer nur das erste Touch-Event gefangen. Alle anderen gingen verloren! Die Suche im Netz blieb leider ohne Erfolg und so habe ich mir eine Alternative überlegt.

  1. public class MyMap extends MapActivity {
  2.     private MapController mapController;
  3.     private MapView mapView;
  4.     private int previousMouseAction = 0;
  5.  
  6.     @Override
  7.     public void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.map);
  10.  
  11.         this.setupMap();
  12.     }
  13.  
  14.     @Override
  15.     protected boolean isRouteDisplayed() {
  16.         return false;
  17.     }
  18.  
  19.     protected void setupMap() {
  20.         mapView = (MapView) findViewById(R.id.mapview);
  21.  
  22.         mapController = mapView.getController();
  23.     mapController.setZoom(14);
  24.  
  25.     final LocationOverlay overlay = new LocationOverlay(
  26.         this, mapView
  27.     );
  28.  
  29.     mapView.getOverlays().add(overlay);
  30.     mapView.setBuiltInZoomControls(true);
  31.     mapView.setEnabled(true);
  32.     mapView.setClickable(true);
  33.  
  34.     overlay.setOnTouchListener(new OnTouchListener() {
  35.         @Override
  36.         public boolean onTouch(
  37.             View v, MotionEvent event) {
  38.         final int mouseAction = event.getAction() &         MotionEvent.ACTION_MASK;
  39.  
  40.         if (2 != previousMouseAction &&
  41.             mouseAction == 1) {
  42.             final Intent result = new Intent();
  43.             float[] location = new float[2];
  44.             GeoPoint p = mapView.
  45.                 getProjection().fromPixels(
  46.                     (int) event.getX(),
  47.                     (int) event.getY()
  48.                 );
  49.  
  50.             location[0] = p.getLatitudeE6();
  51.             location[1] = p.getLongitudeE6();
  52.  
  53.             result.putExtra("location", location);
  54.  
  55.             setResult(RESULT_OK, result);
  56.             finish();
  57.         }
  58.  
  59.         previousMouseAction = mouseAction;
  60.         return false;
  61.         }
  62.     });
  63.  
  64.     overlay.enableCompass();
  65.     overlay.enableMyLocation();
  66.     overlay.runOnFirstFix(new Runnable() {
  67.         public void run() {
  68.             mapController.animateTo(
  69.                 overlay.getMyLocation()
  70.             );
  71.         }
  72.     });
  73.     }
  74. }

 

Man und auch frau sieht: Es gibt keine großen Überraschungen im obigen Code…außer Zeile 34 bis 57. Ich stand ja vor der Aufgabe sämtliche Mouse-Events über der Karte zu behandeln. Direkt über die MapView inklusive Zoom-Buttons habe ich es nicht geschafft. Das Overlay (Code siehe unten) war meine Rettung. Ursprünglich wollte ich es nur für die Anzeige von Pins nutzen. Dort kann man auch auf onTouch-Events reagieren und so habe ich einfach den Code etwas umgekrempelt. Da ich auf einen Klick direkt die Koordinaten als Activity-Result zurückliefern wollte, war es das einfachste, dass ich den onTouch-Handler inline definiere, wovon ich sonst noch kein großer Fan bin. Die Overlay-Klasse bringt dafür von Haus aus aber keinen Setter mit, sodass ich die Klasse genau darum erweitert habe.

  1. public class LocationOverlay extends MyLocationOverlay {
  2.     private OnTouchListener onTouchListener;
  3.  
  4.     public LocationOverlay(Context context, MapView mapView){
  5.     super(context, mapView);
  6.     }
  7.  
  8.     public void setOnTouchListener(OnTouchListener l) {
  9.     onTouchListener = l;
  10.     }
  11.  
  12.     @Override
  13.     public boolean onTouchEvent(
  14.         MotionEvent event, MapView mapView) {
  15.     return onTouchListener.onTouch(mapView, event);
  16.     }
  17. }

Die Event-Callback-Methode, die dem Overlay eingeimpft wird, überprüft einfach nur, welche Mouse-Action gerade behandelt wird (0 = Down, 1 = Up, 2 = Move). Da beim Verschieben der Karte auch Mouse-Events geschmissen werden, muss man darauf achten, dass man das Up-Event nur als Klick interpretiert, wenn unmittelbar davor auch ein Down-Event kam. Das löse ich über die Variable previousMouseAction. Wenn das gewünschte Event ausgelöst wurde, suche ich über die Koordinaten des Events die Koordinaten auf der Karte heraus. Diese Koordinaten kann man dann später über die Geocoder-Klasse in eine Adresse übersetzen lassen.

Ich hoffe, dass diese Lösung dem Einen oder Anderen hilft. Sollte jemand eine Alternative haben: Bitte her damit! Wie gesagt, ich stehe noch ganz am Anfang meiner Android-Karriere 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.