teaching machines

Homework 1 – FiftyStates with MapView

September 30, 2011 by . Filed under cs491 mobile, fall 2011, postmortems.

FiftyStates is a simple quiz application that asks “What’s the capital of ______?” again and again, in multiple-choice format. Users can change the number of questions in each round and toggle sound effects on and off via a PreferenceActivity. Additionally, users can view a “hint” map during play, and a “study” map after a round is complete.

The interface is very simple. Four multiple-choice options are presented in a 2×2 table of buttons with the question above. Below the answers is a toggle button to show and hide the hint map. When toggled, the hint map displays a Google map zoomed to the location of the correct answer. At the end of each round, a simple summary dialog is presented with buttons to play a new round or view a study map. The study map displays the United States with OverlayItems for each state capital from the current round’s questions.

 

Sounds

When an answer is tapped, the next question is presented right away, and the user hears either a happy dinging sound or “uh-oh” thudding sound based on their answer. On wrong answers a toast is presented with the correct answer. I found a fun 8-bit sound effect generator called sfxr. Y’all might enjoy it.

Rather than using a MediaPlayer directly, I use a SoundPool object. You can load multiple sound files into a SoundPool, so it’s handy for applications with lots of little bleeps and bloops. Loading a sound file returns an integer that serves as your key to access that sound later on. The SoundPool.play() method offers good granularity, too. You can control left and right volume levels independently, looping, playback speed, and a priority (in case system resources are limited).


// Initializing and adding sounds to a SoundPool
SoundPool soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
int soundCorrectAnswer = soundPool.load(this, R.raw.correct_answer_sound, 1);
int soundWrongAnswer   = soundPool.load(this, R.raw.wrong_answer_sound,   1);

// Playing sounds.
soundPool.play(soundCorrectAnswer, 1, 1, 0, 0, 1);

MapView

The main data object I implemented (State) has a name, capital city, and a GeoPoint representing the latitude and longitude of the capital city. When the hint map is toggled on, a MapView is set to visible and its corresponding MapController object is told to animate to that location and zoom to a pre-determined level. The Hello, MapView tutorial guides you through getting a Google Maps API key (really easy), what needs to be declared in the manifest, and including a MapView in a layout XML file.

Once you have a MapView in a layout file, you can findByID() and send it messages just like other Views. Generally, things about the MapView are methods on MapView, and things you want the MapView to do are generally methods sent to that MapView’s MapController object.

MapView mapView = (MapView) findViewById(R.id.mapview);
MapController = mapController = mapView.getController();

mapView.setVisibility(View.VISIBLE);
mapView.setBuiltInZoomControls(true);

GeoPoint point = new GeoPoint(0,0);
mapController.animateTo(point);
mapController.setZoom(7);

OverlayItem and ItemizedOverlay

The Hello, MapView tutorial also walks you through adding OverlayItems to a MapView by way of subclassing ItemizedOverlay. In that class, you can do things like set the default marker image (like the purple star PNG I scribbled out), and handle onTap events (for example, to display an AlertDialog with info about that particular OverlayItem).

 

Each MapView has a list of Overlays, and once you have that ItemizedOverlay subclass, you can leverage it to fit your data.

List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear(); // remove any remaining Overlays from previous rounds
mapView.invalidate(); // invalidate so the mapView will be redrawn (effectively removing old Overlays from the view)

Drawable drawable = this.getResources().getDrawable(R.drawable.star_marker);
StudyMapItemizedOverlay itemizedOverlay = new StudyMapItemizedOverlay(drawable, this);

for (State s : states) {
  // Grab the info you need for the new OverlayItem
  String stateName = state.getName();
  String capital = state.getCapital();
  GeoPoint point = state.getLocation();
  // Create an OverlayItem based on that GeoPoint and a descriptive snippet
  OverlayItem overlay = new OverlayItem(point, capital + ", " + name, null);
  // Wrap that in your ItemizedOverlay subclass
  itemizedOverlay.addOverlay(overlay);
  // …and add it to this MapView's list of Overlays
  mapOverlays.add(itemizedOverlay);
}