Mobile Phone Handheld Hardware Hardware Rick Rogers John Lombardo O'Reilly Media, Inc. O'Reilly Media Android Application Development, 1st Edition9.5. Working with MapViewsThe MapView encapsulates a lot of very complex mapping software and is available for
you in your Android applications—for free. Here are some of the things you
can do with a MapView, with only a little programming on your part: Show a street map of any area in the world, with up-to-date
mapping information courtesy of Google Change the MapView to show:
Street view Photographs taken at street level for many areas in North
America
Satellite view An aerial, photographic view of the area
Traffic view Real-time traffic information superimposed on the map or satellite
views
Move the map under program control Plot your own graphics in overlays on top of the map Respond to user touch events on the map
9.5.1. MapView and MyLocationOverlay InitializationThe map in MicroJobs has two modes: At startup, and when we select "Current Location" from the
Spinner, we want to display a map of our current location, and we
want that map to track us as we move around. For this map we will
use the MyLocationOverlay class. When we select a specific location from the Spinner, we want
to display a map of that location, turn off location updates, and
not track movement.
Let's look again at the code in MicroJobs.java that initializes the MapView
and the MyLocationOverlay that tracks our current location: @Override
public void onCreate(Bundle savedInstanceState) {
...
mvMap = (MapView) findViewById(R.id.mapmain);
// get the map controller
final MapController mc = mvMap.getController();
mMyLocationOverlay = new MyLocationOverlay(this, mvMap);
mMyLocationOverlay.runOnFirstFix(
new Runnable() {
public void run() {
mc.animateTo(mMyLocationOverlay.getMyLocation());
mc.setZoom(16);
}
});
Drawable marker = getResources().getDrawable(R.drawable.android_tiny_image);
marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());
mvMap.getOverlays().add(new MJJobsOverlay(marker));
mvMap.setClickable(true);
mvMap.setEnabled(true);
mvMap.setSatellite(false);
mvMap.setTraffic(false);
mvMap.setStreetView(false);
// start out with a general zoom
mc.setZoom(16);
...
/**
* Required method to indicate whether we display routes
*/
@Override
protected boolean isRouteDisplayed() { return false; }Here are some of the highlights of the code: MyLocationOverlay encapsulates a wealth of location and mapping
code. In our single call to the constructor we: Ask Android to figure out what location providers are
available in our environment (GPS, Cell ID, triangulation). Connect to the "best" of those location providers. Ask the location provider to send us periodic location updates
as our handset moves. Link to routines that will automatically move our map as
needed to track any changes in location.
MyLocationOverlay also allows us to place a compass rose on the
MapView and have that updated as well, but we won't be using that in
MJAndroid. The map attributes set by the code are:
setClickable We want users to be able to click (tap) on a job to cause
MJAndroid to display more detail about that job, so we set this to
true.
setEnabled This method is actually inherited from
android.view.View. Google doesn't tell us exactly
what this means in the case of a MapView, but presumably it
enables the standard map functions (zooming, panning,
etc.).
setSatellite Setting this flag adds a satellite view from the composite map,
whereas clearing the flag removes the view. To start with, we
don't want the satellite information on the map.
setTraffic Similarly, setting or clearing this flag adds or removes
current traffic information from the map, respectively. Again, we
don't want to start with traffic information on the map.
setStreetView We don't want street views right now either, although we'll
let the user enable them later.
Android maps come already equipped with support for zooming in
and out. The "i" key zooms in on the map, and the "o" key zooms out.
Maps can also zoom in and out under program control, through the
MapController. There are several methods defined for zooming, all via the
MapController. Android defines 21 zoom levels for maps. At zoom level
1, the equator of the earth is 256 pixels long. Every step up in zoom
level multiplies that by 2. Google warns that the higher-resolution
maps are not available worldwide. All of the zoom methods clamp the
zoom level to the range 1 through 21 if you ask MapController to go
beyond those limits. The methods that control zoom, along with their parameters,
are:
zoomIn Zooms in one level.
zoomOut Zooms out one level.
setZoom(int
zoomlevel) Zooms to the given level, restricting it to the range 1 to
21.
zoomInFixing(int xpixel, int
ypixel), zoomOutFixing(int xpixel, int ypixel) Zoom in one level, but keep the given point fixed on
the screen. Normally when you zoom in and out, the center of the
screen is the only point that stays fixed. These routines let
you pick any point on the map to be the fixed point.
zoomToSpan(int latSpanE6, int
longSpanE6) Attempts to zoom so the given span is displayed on the
map. What it actually does is select the zoom level that is the
closest match for the span requested. The latitude and longitude
span parameters are expressed as integers with a value
106 times the actual value in
degrees. For instance, a latitude/longitude span of 2.5 degrees
by 1.0 degrees would be expressed as zoomToSpan(2500000,
1000000).
|
9.5.2. Pausing and Resuming a MapActivityMobile applications have unique requirements, due mostly to the
constrained resources available to execute applications. For now let's
focus on MapActivities and talk about a way we can help save battery
power. The good news is that Android makes it pretty easy. In a mobile environment, battery life is everything, and if we're not the application that is currently being
displayed, we want to do everything we can to minimize the power we
consume. You recall from the discussion of the Android lifecycle (Chapter 1) that when an Activity (such as
MicroJobs) starts another Activity (such as MicroJobsList) the new Activity takes
over the screen, and the calling Activity gets pushed onto a stack of
Activities that are waiting to run. At that time, Android calls the
onPause() routine in the calling Activity so it can
prepare itself to go into hibernation. At this point in MicroJobs.java (or just about any MapActivity
that uses location updates), we want to turn off location updates. Doing
so will at least save the cycles devoted to doing the update, and may
allow the handset to save even more power by putting the location
provider in a quiescent state. When the called Activity (in our case, MicroJobsList) exits and
the calling Activity is popped off the stack and takes control of the
screen, the framework calls the onResume method in the
calling Activity. In a MapActivity, we want to turn on location updates
again when this method is invoked. In MicroJobs, the onPause and onResume
methods are straightforward: /**
* @see com.google.android.maps.MapActivity#onPause()
*/
@Override
public void onPause() {
super.onPause();
mMyLocationOverlay.disableMyLocation();
}
/**
* @see com.google.android.maps.MapActivity#onResume()
*/
@Override
public void onResume() {
super.onResume();
mMyLocationOverlay.enableMyLocation();
}Note that if we'd had a compass rose as part of our
MyLocationOverlay, we would have to disable and enable it as well.
Otherwise, the system would be updating the direction of the compass
rose even when it wasn't being displayed, thereby wasting cycles and
battery power. 9.5.3. Controlling the Map with Menu ButtonsWe want to give the user the ability to turn on satellite,
traffic, and street views of the map. In addition, we'll throw in a few
menu buttons to enable zooming and another way of getting to the Jobs
List. Android has a sophisticated set of menu capabilities that includes
three types of menus (options, context, and submenus), each with its own
capabilities, icon menu buttons, and other advanced features. We just
use text-based menu buttons, and so we need to do two things: Create the menu of buttons that will be displayed. Catch the menu events and invoke appropriate actions.
The following code creates the menu in MicroJobs.java: /**
* Set up menus for this page
*
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean supRetVal = super.onCreateOptionsMenu(menu);
menu.add(Menu.NONE, 0, Menu.NONE, getString(R.string.map_menu_zoom_in));
menu.add(Menu.NONE, 1, Menu.NONE, getString(R.string.map_menu_zoom_out));
menu.add(Menu.NONE, 2, Menu.NONE, getString(R.string.map_menu_set_satellite));
menu.add(Menu.NONE, 3, Menu.NONE, getString(R.string.map_menu_set_map));
menu.add(Menu.NONE, 4, Menu.NONE, getString(R.string.map_menu_set_traffic));
menu.add(Menu.NONE, 5, Menu.NONE, getString(R.string.map_menu_show_list));
return supRetVal;
}We create menu buttons by overriding the
onCreateOptionsMenu method, where we are passed a menu parameter for the Activity's
menu. After dutifully allowing the superclass a chance to do what it
needs to do, we simply add items (buttons) to the menu using
menu.add. The version of menu.add that we've
chosen takes four parameters:
int groupid Android allows you to group menu items so you can quickly
change the whole menu at once. We don't have a need for that in
MicroJobs, so Menu.NONE says we don't need it.
int itemid We need a unique identifier for this menu item so we can
tell later whether it was picked.
int order The itemid we defined in the second
parameter does not imply order. If we cared about the order in
which the items were presented, we'd do that with this parameter.
Since we don't care, we use Menu.NONE again.
int titleRes The ID of the string resource we want to use for the button
title. Note that this is an integer, not a string, so the menu
strings need to be predefined in string.xml,
under the res directory. You recall that
Android takes care of compiling the strings in res/strings.xml into a .java file (R.java) that assigns an integer to each
string. The getString method retrieves that integer
for you (despite the name, the method returns an integer, not a
string).
To catch the menu events, we override the
onOptionsItemSelected method: /**
* @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0:
// Zoom in
zoomIn();
return true;
case 1:
// Zoom out
zoomOut();
return true;
case 2:
// Toggle satellite views
mvMap.setSatellite(!mvMap.isSatellite());
return true;
case 3:
// Toggle street views
mvMap.setStreetView(!mvMap.isStreetView());
return true;
case 4:
// Toggle traffic views
mvMap.setTraffic(!mvMap.isTraffic());
return true;
case 5:
// Show the job list activity
startActivity(new Intent(MicroJobs.this, MicroJobsList.class));
return true;
}
return false;
}We are passed the selected MenuItem, and the switch
has a case for each button that we defined for the menu. The code for
each case is similar to code that we've seen before. 9.5.4. Controlling the Map with the KeyPadSome users might prefer to control the map through the keypad (generally one "click," versus
two "clicks" to cause a Menu event). Enabling this behavior also
demonstrates how to respond to KeyPad events in general, so we've added
some code to zoom in, zoom out, and back out of the current
Activity: /**
* @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP: // zoom in
zoomIn();
return true;
case KeyEvent.KEYCODE_DPAD_DOWN: // zoom out
zoomOut();
return true;
case KeyEvent.KEYCODE_BACK: // go back (meaning exit the app)
finish();
return true;
default:
return false;
}
}To catch key down events, we simply override
onKeyDown and provide a switch for the different keys that are of interest.
In addition to the keycodes you would expect (KEYCODE_A,
...KEYCODE_Z and things like KEYCODE_SPACE,
KEYCODE_SHIFT_LEFT, and KEYCODE_SHIFT_RIGHT), Android
includes keycodes that may or may
not appear on any particular
device (e.g., KEYCODE_CAMERA and
KEYCODE_VOLUME_UP). A complete set of keycodes can be found at .
 |