Login     Sign Up
Cyril Sermon (@admin)
9 months ago
55 Views

Previously in this chapter, you saw how to add the Zoom View to a Map View by pinning it to a specific screen location. You can pin any View-derived object to a Map View (including layouts and other View Groups), attaching it to either a screen position or a geographical map location.

In the latter case, the View will move to follow its pinned position on the map, effectively acting as an interactive map marker. As a more resource-intensive solution, this is usually reserved for supplying the detail “balloons” often displayed on mashups to provide further detail when a marker is clicked.

Both pinning mechanisms are implemented by calling addView on the MapView, usually from the onCreate or onRestore methods within the MapActivity containing it. Pass in the View you want to pin and the layout parameters to use.

The MapView.LayoutParams parameters you pass in to addView determine how, and where, the View is added to the map.

To add a new View to the map relative to the screen, specify a new MapView.LayoutParams including arguments that set the height and width of the View, the x/y screen coordinates to pin to, and the align-ment to use for positioning, as shown below:

int y = 10;

int x = 10;

MapView.LayoutParams screenLP;

screenLP = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT,

x, y,

MapView.LayoutParams.TOP_LEFT);

EditText editText1 = new EditText(getApplicationContext()); editText1.setText(“Screen Pinned”);

mapView.addView(editText1, screenLP);

To pin a View relative to a physical map location, pass four parameters when constructing the new MapView LayoutParams, representing the height, width, GeoPoint to pin to, and the layout alignment.

Double lat = 37.422134*1E6;

Double lng = -122.084069*1E6;

GeoPoint geoPoint = new GeoPoint(lat.intValue(), lng.intValue());

MapView.LayoutParams geoLP;

geoLP = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT,

geoPoint,

MapView.LayoutParams.TOP_LEFT);

EditText editText2 = new EditText(getApplicationContext()); editText2.setText(“Location Pinned”);

mapView.addView(editText2, geoLP);

Panning the map will leave the first TextView stationary in the upper left corner, while the second TextView will move to remain pinned to a particular position on the map.

To remove a View from a MapView, call removeView, passing in the View instance you wish to remove, as shown below:

mapView.removeView(editText2);

Mapping Earthquakes Example

The following step-by-step guide demonstrates how to build a map-based Activity for the Earthquake project you started in Chapter 5. The new MapActivity will display a map of recent earthquakes using techniques you learned within this chapter.

Create a new earthquake_map.xml layout resource that includes a MapView, being sure to include an android:id attribute and a android:apiKey attribute that contains your Android Maps API key.

<?xml version=”1.0” encoding=”utf-8”?>

<LinearLayout

xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”>

<com.google.android.maps.MapView

android:id=”@+id/map_view”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

android:enabled=”true”

android:clickable=”true”

android:apiKey=”myapikey”

/>

</LinearLayout>

Create a new EarthquakeMap Activity that inherits from MapActivity. Use setContentView within onCreate to inflate the earthquake_map resource you created in Step 1.

package com.paad.earthquake;

import android.os.Bundle;

import com.google.android.maps.MapActivity;

public class EarthquakeMap extends MapActivity {

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.earthquake_map);

}

@Override

protected boolean isRouteDisplayed() {

return false;

}

}

Update the application manifest to include your new EarthquakeMap Activity and import the map library.

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android” package=”com.paad.earthquake”>

<application android:icon=”@drawable/icon”>

<activity

android:name=”.Earthquake”

android:label=”@string/app_name”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” /> <category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

<activity android:name=”.Preferences” android:label=”Earthquake Preferences”/>

<activity android:name=”.EarthquakeMap”

android:label=”View Earthquakes”/>

<provider android:name=”.EarthquakeProvider”

android:authorities=”com.paad.provider.earthquake” /> <uses-library android:name=”com.google.android.maps”/>

</application>

<uses-permission android:name=”android.permission.INTERNET”/> </manifest>

Add a new menu option to the Earthquake Activity to display the EarthquakeMap Activity.

4.1. Start by adding a new string to the strings.xml resource for the menu text.

<?xml version=”1.0” encoding=”utf-8”?>

<resources>

<string name=”app_name”>Earthquake</string>

<string name=”quake_feed”>

http://earthquake.usgs.gov/eqcenter/catalogs/1day-M2.5.xml </string>

<string name=”menu_update”>Refresh Earthquakes</string>

<string name=”auto_update_prompt”>Auto Update?</string>

<string name=”update_freq_prompt”>Update Frequency</string>

<string name=”min_quake_mag_prompt”>Minimum Quake Magnitude</string>

<string name=”menu_preferences”>Preferences</string>

<string name=”menu_earthquake_map”>Earthquake Map</string> </resources>

4.2. Then add a new menu identifier before modifying the onCreateOptionsMenu han-dler to add the new Menu Item. It should use the text defined in Step 4.1, and when selected, it should fire an Intent to explicitly start the EarthquakeMap Activity.

static final private int MENU_EARTHQUAKE_MAP = Menu.FIRST+2;

@Override

public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu);

menu.add(0, MENU_UPDATE, Menu.NONE, R.string.menu_update); menu.add(0, MENU_PREFERENCES, Menu.NONE, R.string.menu_preferences);

Intent startMap = new Intent(this, EarthquakeMap.class); menu.add(0, MENU_EARTHQUAKE_MAP,

Menu.NONE,

R.string.menu_earthquake_map).setIntent(startMap);

return true;

}

Now create a new EarthquakeOverlay class that extends Overlay. It will draw the position and magnitude of each earthquake on the Map View.

package com.paad.earthquake;

import java.util.ArrayList;

import android.database.Cursor;

import android.database.DataSetObserver;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Point;

import android.graphics.RectF;

import com.google.android.maps.GeoPoint;

import com.google.android.maps.MapView;

import com.google.android.maps.Overlay;

import com.google.android.maps.Projection;

public class EarthquakeOverlay extends Overlay {

@Override

public void draw(Canvas canvas, MapView mapView, boolean shadow) { Projection projection = mapView.getProjection();

if (shadow == false) {

// TODO: Draw earthquakes

}

}

}

5.1. Add a new constructor that accepts a Cursor to the current earthquake data, and store that Cursor as an instance variable.

Cursor earthquakes;

public EarthquakeOverlay(Cursor cursor, ContentResolver resolver) { super();

earthquakes = cursor;

}

5.2. Create a new refreshQuakeLocations method that iterates over the results Cursor and extracts the location of each earthquake, extracting the latitude and longitude before storing each coordinate in a List of GeoPoints.

ArrayList<GeoPoint> quakeLocations;

private void refreshQuakeLocations() {

if (earthquakes.moveToFirst())

do {

Double lat;

lat = earthquakes.getFloat(EarthquakeProvider.LATITUDE_COLUMN) * 1E6; Double lng;

lng = earthquakes.getFloat(EarthquakeProvider.LONGITUDE_COLUMN) * 1E6;

GeoPoint geoPoint = new GeoPoint(lng.intValue(), lat.intValue());

quakeLocations.add(geoPoint);

} while(earthquakes.moveToNext());

}

5.3. Call refreshQuakeLocations from the Overlay’s constructor. Also register a DataSetObserver on the results Cursor that refreshes the Earthquake Location list if a change in the Earthquake Cursor is detected.

public EarthquakeOverlay(Cursor cursor) {

super();

earthquakes = cursor;

quakeLocations = new ArrayList<GeoPoint>();

refreshQuakeLocations();

earthquakes.registerDataSetObserver(new DataSetObserver() {

@Override

public void onChanged() {

refreshQuakeLocations();

}

});

}

5.4. Complete the EarthquakeOverlay by overriding the draw method to iterate over the list of GeoPoints, drawing a marker at each earthquake location. In this example, a simple red circle is drawn, but it could easily be modified to include additional information, such as by adjusting the size of each circle based on the magnitude of the quake.

int rad = 5;

@Override

public void draw(Canvas canvas, MapView mapView, boolean shadow) { Projection projection = mapView.getProjection();

Create and setup your paint brush Paint paint = new Paint(); paint.setARGB(250, 255, 0, 0); paint.setAntiAlias(true); paint.setFakeBoldText(true);

if (shadow == false) {

for (GeoPoint point : quakeLocations) {

Point myPoint = new Point();

projection.toPixels(point, myPoint);

RectF oval = new RectF(myPoint.x-rad, myPoint.y-rad, myPoint.x+rad, myPoint.y+rad);

canvas.drawOval(oval, paint);

}

}

}

Return to the EarthquakeMap class. Within the onCreate method, create a Cursor that returns the earthquakes you want to display on the map. Use this Cursor to create a new EarthquakeOverlay before adding the new instance to the Map View’s list of overlays.

Cursor earthquakeCursor;

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.earthquake_map);

String earthquakeURI = EarthquakeProvider.CONTENT_URI; earthquakeCursor = getContentResolver().query(earthquakeURI,

null, null, null, null);

MapView earthquakeMap = (MapView)findViewById(R.id.map_view); EarthquakeOverlay eo = new EarthquakeOverlay(earthquakeCursor); earthquakeMap.getOverlays().add(eo);

}

Finally, override onResume to call requery on the Earthquake result set whenever this Activity becomes visible. Also, override onPause and onDestroy to optimize use of the Cursor resources.

@Override

public void onResume() {

earthquakeCursor.requery();

super.onResume();

}

@Override

public void onPause() {

earthquakeCursor.deactivate();

super.onPause();

}

@Override

public void onDestroy() {

earthquakeCursor.close();

super.onDestroy();

}

If you run the application and select Earthquake Map from the main menu, your application should appear as shown in Figure 7-8.

Summar y

Location-based services, the Geocoder, and MapViews are available to create intuitive, location-aware applications that feature geographical information.

This chapter introduced the Geocoder and showed how to perform forward and reverse geocoding lookups to translate between map coordinates and street addresses. You were introduced to location-based services, used to find the current geographical position of the device. You also used them to track movement and create proximity alerts.

Then you created interactive map applications. Using Overlays and Views, you annotated MapViews with 2D graphics, as well as markers in the form of OverlayItems and Views (including ViewGroups and layouts).

In Chapter 8, you’ll learn how to work from the background. You’ll be introduced to the Service com-ponent and learn how to move processing onto background threads. To interact with the user while hidden from view, you’ll use Toasts to display transient messages and the Notification Manager to ring, vibrate, and flash the phone.