Monday, August 16, 2010
Alpha 1 Complete
Well, after two very unproductive weeks, I'm finally able to say that I've achieved my first major milestone. My first Android application is in its first alpha iteration. At this point it's all about refining it's current behavior.
Wednesday, August 4, 2010
Testing if a User has Clicked One of Your MapView OverlayItems
If you are interested in developing Android applications which have a mapping component, you have undoubtedly heard of Google's mapping API. It's an incredibly usefull API, but has some holes.
For example, they haven't released code to detect if one of your OverlayItems has been clicked by the user. Actually, they've documented that the ItemizedOverlay class has a method called hitTest which does not seem to work properly.
Very quickly, here's a method you can implement in your ItemizedOverlay which will check if any of your OverlayItems have been clicked. The meat of this code is the runHitTest method. Be sure to notice, however, that we've saved a copy of the defaultMarker that was passed in when the SomeItemizedOverlay class was instantiated. This is important, because we need to know what the marker's bounds are. These bounds can be changed. Take a look at boundCenterBottom or boundCenter, for example.
I hope you find this useful!
Edit: You could alternately use this version of runHitTest(). It responds only to taps, where the other version responded to any gesture that began on an instance of SomeOverlay.
Where
For example, they haven't released code to detect if one of your OverlayItems has been clicked by the user. Actually, they've documented that the ItemizedOverlay class has a method called hitTest which does not seem to work properly.
Very quickly, here's a method you can implement in your ItemizedOverlay which will check if any of your OverlayItems have been clicked. The meat of this code is the runHitTest method. Be sure to notice, however, that we've saved a copy of the defaultMarker that was passed in when the SomeItemizedOverlay class was instantiated. This is important, because we need to know what the marker's bounds are. These bounds can be changed. Take a look at boundCenterBottom or boundCenter, for example.
import java.util.ArrayList;
import android.app.Activity;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.Projection;
public class SomeItemizedOverlay extends ItemizedOverlay<SomeOverlay> {
private ArrayList<SomeOverlay> mOverlays = new ArrayList<SomeOverlay>();
private Activity mContext;
private Drawable mMarker;
private SomeItemizedOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
mMarker = boundCenterBottom(defaultMarker);
}
public SomeItemizedOverlay(Drawable defaultMarker, Activity ctx) {
super(boundCenterBottom(defaultMarker));
mContext = ctx;
mMarker = boundCenterBottom(defaultMarker);
}
@Override
protected SomeOverlay createItem(int i) {
return mOverlays.get(i);
}
@Override
public int size() {
return mOverlays.size();
}
public void addOverlay(SomeOverlay overlay) {
mOverlays.add(overlay);
populate();
}
public boolean onTap(GeoPoint p, MapView v) {
// Create and add an OverlayItem
}
public boolean onTouchEvent(MotionEvent e, MapView v) {
return runHitTest(e,v);
}
private boolean runHitTest(MotionEvent e, MapView v) {
if( e.getAction() != MotionEvent.ACTION_DOWN )
return false;
int x = new Float(e.getX()).intValue();
int y = new Float(e.getY()).intValue();
Projection proj = v.getProjection();
for(int i = 0; i < mOverlays.size(); i++) {
// We need to translate the geographical location
// of this particular OverlayItem to it's corresponding
// on-screen pixel location.
GeoPoint gpt = mOverlays.get(i).getPoint();
Point loc = proj.toPixels(gpt, null);
// Now we need to translate the clicked pixel (x,y)
// to this OverlayItem's coordinate frame.
int x_ovrly = x - loc.x;
int y_ovrly = y - loc.y;
// Now we need to check if the translated point is in
// the Drawable used to mark this OverlayItem.
if( mMarker.getBounds().contains(x_ovrly, y_ovrly) ) {
Log.v("MyApplication", "hit item: " + i);
return true;
}
}
return false;
}
}
I hope you find this useful!
Edit: You could alternately use this version of runHitTest(). It responds only to taps, where the other version responded to any gesture that began on an instance of SomeOverlay.
private boolean runHitTest(MotionEvent e, MapView v) {
// Only handle the event if it truly represents a tap.
if( ! (e.getAction() == MotionEvent.ACTION_UP && mInteractionState == MotionEvent.ACTION_DOWN) ) {
mInteractionState = e.getAction();
return false;
} else {
mInteractionState = e.getAction();
}
int x = new Float(e.getX()).intValue();
int y = new Float(e.getY()).intValue();
Projection proj = v.getProjection();
for(int i = 0; i < mOverlays.size(); i++) {
// We need to translate the geographical location
// of this particular OverlayItem to it's corresponding
// on-screen pixel location.
VicinityOverlay ovrly = mOverlays.get(i);
GeoPoint gpt = ovrly.getPoint();
Point loc = proj.toPixels(gpt, null);
// Now we need to translate the clicked pixel (x,y)
// to this OverlayItem's coordinate frame.
int x_ovrly = x - loc.x;
int y_ovrly = y - loc.y;
// Now we need to check if the translated point is in
// the Drawable used to mark this OverlayItem.
if( mMarker.getBounds().contains(x_ovrly, y_ovrly) ) {
Log.v("MyApplication", "hit item: " + i);
return true;
}
}
return false;
}
Where
mInteractionState is a private int in the SomeItemizedOverlay class.
Google's MapView library for Android phones
Google's MapView library needs some work, or at least it's documentation does. I've spent more hours trying to discover what the library expects from me than I have spent learning a library in a very long time. Problems include NullPointerExceptions if you don't override methods (which are not declared abstract by the way), and various other things.
I've set myself an ambitious timeline to complete the early phases of development on my application, and this is pushing me back a bit. I'm planning to blog what I've learned by trial and error after I hit my Alpha 2 milestone. Hopefully, a few articles will prevent others from loosing time as well.
I've set myself an ambitious timeline to complete the early phases of development on my application, and this is pushing me back a bit. I'm planning to blog what I've learned by trial and error after I hit my Alpha 2 milestone. Hopefully, a few articles will prevent others from loosing time as well.
Subscribe to:
Posts (Atom)