I have very limited experience with Mapview, Overlay and ItemizedOverlay. I've seen some simple code examples that use one drawable for overlays and itemizedOverlays but I'm unsure how to approach these requirements: I want more than one drawable on my map view (example: a star-icon for the map center and some other icon for other overlay items) but I want one to be unclickable (the star). Should I use both Overlay and ItemizedOverlay to achieve this?
Also, my next problem is a matter of presentation: If I have 500 items to display on a map, what is a practical way of displaying that information? Again, I have little experience developing and using map applications.
I faced the same situation a couple of weeks ago.
You should use one ItemizedOverlay for each different drawable, and then add overlayItems to the ItemizedOverlay.
The most convenient way is to extend ItemizedOverlay, so you can define a marker and a click behavoir for each style you need.
For the second part, for performance concerns, you shouldn't populate your map with all your 500 items once at a time. I used a system that dynamically adds to the map markers that belongs to the displayed scope of map.
Here is the snippet of my ItemizedOverlay that could be useful for both of your questions :
private class MarkerItemized_Overlay extends ItemizedOverlay<OverlayItem> {
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
Context mContext;
public MarkerItemizedOverlay(Drawable marker, Context context) {
super(boundCenterBottom(marker));
mContext = context;
}
public void addOverlay(OverlayItem overlay) {
if (!mOverlays.contains(overlay)) {
mOverlays.add(overlay);
populate();
}
}
#Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
#Override
public int size() {
return mOverlays.size();
}
#Override
protected boolean onTap(final int index) {
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.setNegativeButton("Annuler",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){}
});
dialog.setPositiveButton("Naviguer",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){ showDirections(mOverlays.get(index).getPoint()); }
});
dialog.setInverseBackgroundForced(true);
dialog.show();
return true;
}
public boolean contains(Store store) {
return mOverlays.contains(store);
}
#Override
public boolean onTouchEvent(MotionEvent e, MapView mapView) {
if (e.getAction() == MotionEvent.ACTION_UP) {
GeoPoint newCenter = mapView.getMapCenter();
int minLat, maxLat, minLng, maxLng;
minLat = mapCenter.getLatitudeE6() - mapView.getLatitudeSpan()/2;
maxLat = mapCenter.getLatitudeE6() + mapView.getLatitudeSpan()/2;
minLng = mapCenter.getLongitudeE6() - mapView.getLongitudeSpan()/2;
maxLng = mapCenter.getLongitudeE6() + mapView.getLongitudeSpan()/2;
if (newCenter.getLatitudeE6() > maxLat ||
newCenter.getLatitudeE6() < minLat ||
newCenter.getLongitudeE6() > maxLng ||
newCenter.getLongitudeE6() < minLng)
{
mapCenter = mapView.getMapCenter();
Location mapCenterLoc = new Location(providerName);
mapCenterLoc.setLatitude(newCenter.getLatitudeE6()/1E6);
mapCenterLoc.setLongitude(newCenter.getLongitudeE6()/1E6);
Store[] newClosestStores = storeDB.getClosestStores(mapCenterLoc);
for (int i = 0; i < newClosestStores.length; i++)
if (! itemizedOverlay.contains(newClosestStores[i]))
setMarker(newClosestStores[i]);
}
}
return super.onTouchEvent(e, mapView);
}
}
Related
I use
+android 4.2
+osmdroid-android-3.0.9
+sherlock action bar
I need show on map item. On click at this item show dialog. But has trouble onItemSingleTapUp raised with offset.
osmapview = (MapView) findViewById(R.id.osmapview);
osmapview.setBuiltInZoomControls(true);
osmapview.setTileSource(TileSourceFactory.MAPNIK);
osmapview.setClickable(false);
final MapController mc = osmapview.getController();
mc.setZoom(14);
ArrayList<OverlayItem> overlayItems = new ArrayList<OverlayItem>();
CustomOverLay overlays = new CustomOverLay(overlayItems, getResources().getDrawable(R.drawable.marker96));
GeoPoint p = new GeoPoint(48.427677, 35.239871);
OverlayItem overlayItem = new OverlayItem("Title1", "Description1", p);
overlays.addItem(overlayItem);
public class CustomOverLay extends ItemizedIconOverlay<OverlayItem> {
public CustomOverLay(ArrayList<OverlayItem> overlayItems, Drawable drawable) {
super(overlayItems, drawable, new OnItemGestureListener<OverlayItem>() {
#Override
public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
L.e("++" + 1);
return false;
}
#Override
public boolean onItemLongPress(final int index, final OverlayItem item) {
L.e("++" + 2);
return false;
}},
new DefaultResourceProxyImpl(getApplicationContext()));
}
#Override
protected boolean onSingleTapUpHelper(int index, OverlayItem item, MapView mapView) {
L.e("+ " + item.mTitle);
return super.onSingleTapUpHelper(index, item, mapView);
}
}
map drawed - ok
marker drawed - ok
tap on marker - failed
if tap bottom (very bottom) of marker - ok
What is the cause the offset?
Turn off hardware acceleration in your manifest or in code. See the samples application for examples on how to do this. There is better hardware acceleration support in 3.0.10.
It's error in osmdroid-android-3.0.9
use osmdroid-android-3.0.8
Issue
Somehow when I tap on the Icon I added to the Map the onTap Event is not called can somebody tell me why :S? Its a overlay with all Users I add to the Map they all have the same Icon but all diffrent Titles. But when I tap nothing happens I also tried Logging it if something happens with Log.d but nothing happens. This is the code:
private class MyUsersOverlay extends ItemizedOverlay<OverlayItem> {
private List<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
public MyUsersOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
#Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
#Override
public int size() {
return mOverlays.size();
}
public void addOverlayItem(int lat, int lon, String title, String... message) {
GeoPoint point = new GeoPoint(lat, lon);
OverlayItem overlayItem = new OverlayItem(point, title, null);
addOverlayItem(overlayItem);
}
public void addOverlayItem(OverlayItem overlayItem) {
mOverlays.add(overlayItem);
populate();
}
#Override
protected boolean onTap(int index)
{
Log.d("Test Message", "It works");
OverlayItem item = mOverlays.get(index);
//Do stuff here when you tap, i.e. :
//AlertDialog.Builder dialog = new AlertDialog.Builder(cMainActivity);
//dialog.setTitle(item.getTitle());
//dialog.setMessage(item.getSnippet());
//dialog.show();
Toast.makeText(cMainActivity, item.getTitle(),
Toast.LENGTH_SHORT).show();
return true;
}
#Override
public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow)
{
super.draw(canvas, mapView, shadow);
if (shadow == false)
{
//cycle through all overlays
for (int index = 0; index < mOverlays.size(); index++)
{
OverlayItem item = mOverlays.get(index);
// Converts lat/lng-Point to coordinates on the screen
GeoPoint point = item.getPoint();
Point ptScreenCoord = new Point() ;
mapView.getProjection().toPixels(point, ptScreenCoord);
//Paint
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(10);
paint.setARGB(150, 0, 0, 0); // alpha, r, g, b (Black, semi see-through)
//show text to the right of the icon
canvas.drawText(item.getTitle(), ptScreenCoord.x, ptScreenCoord.y+10, paint);
}
}
}
Did you remember to put mapView.setClickable(true); in your oncreate() methode?
The problem isn't in the code above, but somewhere else. You have another view stealing the touch events from your ´ItemizedOverlay`.
Possibilities
You may be playing with dispatchTouchEvent() in your map activity and returning true all the time, informing that events have been used and no further dispatch should occour.
In the layout where you define MapView, you may have added a view after MapView that is parcialy covering it. Motion events are dispatched starting from last added view.
You may have added another MapView overlay that is covering the ItemizedOverlay.
It can be an issue if you want to click on an overlay AND have events for clicking on the general mapview. If that is what you are trying to achieve then check out this tutorial. Even if you are having a different issue the tutorial might help you fix your problem.
Once again I seek the wisdom of my betters...
I'm working on an Android app that uses Google Maps and programmatically puts markers on the map based on status info from a file read from the web.
The problem I have is that each marker is drawn twice. Once in the right spot and once a little below (the top line of the second marker is in line with the bottom of the first marker).
private class SitesOverlay extends ItemizedOverlay<OverlayItem> {
private List<OverlayItem> items=new ArrayList<OverlayItem>();
private Drawable marker=null;
public SitesOverlay(Drawable marker) {
super(marker);
this.marker=marker;
try {
data = getData();
} catch (MalformedURLException e) {
//
}
if (!data.equals("")) {
String[] msg = data.split(NEWLINE);
for (Integer i = 0; i < msg.length; i++) {
items.add(new OverlayItem(
getPoint(lat.get(suburb), lng.get(suburb)),
msg[i], msg[i]));
}
}
populate();
map.postInvalidate();
}
#Override
protected OverlayItem createItem(int i) {
return(items.get(i));
}
#Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
super.draw(canvas, mapView, false);
//boundCenterBottom(marker);
}
#Override
protected boolean onTap(int i) {
Toast.makeText(getBaseContext(),
items.get(i).getSnippet(),
Toast.LENGTH_LONG).show();
map.getController().setZoom(16 + zoomModifier);
return(true);
}
#Override
public int size() {
return(items.size());
}
}
However, the second marker only becomes evident if I uncomment the boundCenterBottom(marker) in the draw function.
I've been trying to workout what is placing the second marker for days. Can anyone point me in the right direction?
Thanks
EDIT: I forgot to mention that if I touch the screen the marker that is not boundCenterBottom disappears.
Ok, finally worked out what was wrong.
Change
public SitesOverlay(Drawable marker) {
super(marker);
this.marker=marker;
to
public SitesOverlay(Drawable marker) {
super(marker);
boundCenterBottom(marker);
Is there any way to put a pushpin on an android map and when it's touched displays a popup with some extra info?
You need to extend this http://code.google.com/android/add-ons/google-apis/reference/com/google/android/maps/ItemizedOverlay.html.
public class CustomOverlay extends ItemizedOverlay<OverlayItem> {
private Context context;
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
public CustomOverlay(Drawable defaultMarker, Context context) {
super(boundCenterBottom(defaultMarker));
this.context = context;
//after adding things to the overlay, call these:
setLastFocusedIndex(-1);
populate();
}
#Override
protected boolean onTap(int index) {
//called when an item is tapped
return true;
}
#Override
public boolean onTap (final GeoPoint p, final MapView mapV) {
boolean tapped = super.onTap(p, mapV);
if(!tapped){
//you can use this to check for other taps on the custom elements you are drawing
}
return true;
}
#Override
public void draw(Canvas canvas, MapView mapV, boolean shadow){
if(!shadow)
// if you have a custom image you may not want the shadow to be drawn
super.draw(canvas,mapV,shadow);
if(selected != null) {
// selected just means that something was clicked
// it isn't defined in this example
Projection projection = mapV.getProjection();
Point drawPoint = projection.toPixels(selected.getPoint(), null);
//get coordinates so you can do your drawing code afterward
}
}
#Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
#Override
public int size() {
return mOverlays.size();
}
}
This is a very rough sketch of what you need to do. Hope this helps.
This code is using
the Google APIs (level 8).
When I update the OverlayItem to use a custom drawable the Canvas
object seems to draw the pixels in the wrong location. In this
example I'm trying to draw a circle in Louisiana. When viewing the
entire map the circle is drawn off the map. As you zoom into New
Orleans you'll see the circle approach the appropriate latitude and
longitude. The hot spot seems to be in the correct location, no
matter where the circle is being drawn.
If, in the draw method, the canvas restore method is called the circle
draws in the correct location.
Also If the custom drawable is not used, the icon is drawn in the
correct location (without using Canvas "restore").
Below is the code showing this behavior. I tried adding "setBounds"
and "boundCenterBottom", since other people seemed to indicate that
resolved their "wrong location" problems. Though
to be honest I'm not sure why those calls are needed.
=======================================================================
public class MapsActivity extends MapActivity
{
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MapView mapView = (MapView) findViewById(R.id.mapView);
mapView.setBuiltInZoomControls(true);
// Itemized Overlay
List<Overlay> mapOverlays = mapView.getOverlays();
Drawable defaultIcon =
this.getResources().getDrawable(R.drawable.icon);
MyItemizedOverlay itemizedoverlay = new
MyItemizedOverlay(defaultIcon, this);
// Overlay Item
GeoPoint pt = new GeoPoint(30000000, -90000000);
OverlayItem item = new OverlayItem(pt,"New Orleans",
"Louisiana");
// Custom Drawable
CustomDrawable customDrawable = new CustomDrawable(pt,
mapView);
boolean showProblem = true;
if (showProblem)
{
item.setMarker(customDrawable);
}
else
{
item.setMarker(defaultIcon);
}
// Add item we want to overlay
itemizedoverlay.addOverlay(item);
// Add overlay
mapOverlays.add(itemizedoverlay);
}
protected boolean isRouteDisplayed()
{
return false;
}
}
=======================================================================
public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem>
{
private ArrayList<OverlayItem> mOverlays = new
ArrayList<OverlayItem>();
private Context mContext;
public MyItemizedOverlay(Drawable defaultMarker, Context context)
{
super(boundCenterBottom(defaultMarker));
mContext = context;
}
public void addOverlay(OverlayItem item)
{
mOverlays.add(item);
populate();
}
public void removeOverlay(OverlayItem item)
{
mOverlays.remove(item);
}
public void removeOverlay(int item)
{
mOverlays.remove(item);
}
protected OverlayItem createItem(int i)
{
OverlayItem item = mOverlays.get(i);
Drawable drawable = item.getMarker(0);
if (drawable != null)
{
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
drawable.setBounds(0, 0, w, h);
item.setMarker(boundCenterBottom(drawable));
}
return item;
}
public void draw(android.graphics.Canvas canvas, MapView mapView,
boolean shadow)
{
if (shadow)
return;
super.draw(canvas, mapView, shadow);
}
public int size()
{
return mOverlays.size();
}
protected boolean onTap(int index)
{
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new
AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
}
=======================================================================
public class CustomDrawable extends ShapeDrawable
{
private int radius = 10;
private GeoPoint point = null;
private MapView mapView = null;
public CustomDrawable(GeoPoint point, MapView mapView)
{
this.point = point;
this.mapView = mapView;
}
public void draw(Canvas canvas)
{
// TODO This (somewhat) fixes projection problem?
//canvas.restore();
Projection projection = mapView.getProjection();
Point pt = projection.toPixels(point, null);
canvas.drawCircle(pt.x, pt.y, radius,
getPaint());
}
public int getIntrinsicHeight()
{
return 2 * radius;
}
public int getIntrinsicWidth()
{
return 2 * radius;
}
}
=======================================================================
Your CustomDrawable shouldn't be positioning itself with respect to the map. It should just draw itself within its bounds, and not reference the MapView or its Projection. ItemizedOverlay takes care of positioning the Drawable for your OverlayItem.