com.mapbox.mapboxsdk.overlay.ItemizedOverlay Maven / Gradle / Ivy
// Created by plusminus on 23:18:23 - 02.10.2008
package com.mapbox.mapboxsdk.overlay;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import com.mapbox.mapboxsdk.views.MapView;
import com.mapbox.mapboxsdk.views.safecanvas.ISafeCanvas;
import com.mapbox.mapboxsdk.views.safecanvas.ISafeCanvas.UnsafeCanvasHandler;
import com.mapbox.mapboxsdk.views.safecanvas.SafePaint;
import com.mapbox.mapboxsdk.views.util.Projection;
import java.util.ArrayList;
/**
* Draws a list of {@link Marker} as markers to a map. The item with the lowest index is drawn
* as last and therefore the 'topmost' marker. It also gets checked for onTap first. This class is
* generic, because you then you get your custom item-class passed back in onTap().
*
* @author Marc Kurtz
* @author Nicolas Gramlich
* @author Theodore Hong
* @author Fred Eisele
*/
public abstract class ItemizedOverlay extends SafeDrawOverlay implements Overlay.Snappable {
private final ArrayList mInternalItemList;
protected boolean mDrawFocusedItem = true;
private Marker mFocusedItem;
private boolean mPendingFocusChangedEvent = false;
private OnFocusChangeListener mOnFocusChangeListener;
private static SafePaint mClusterTextPaint;
/**
* Method by which subclasses create the actual Items. This will only be called from populate()
* we'll cache them for later use.
*/
protected abstract Marker createItem(int i);
/**
* The number of items in this overlay.
*/
public abstract int size();
public ItemizedOverlay() {
super();
if (mClusterTextPaint == null) {
mClusterTextPaint = new SafePaint();
mClusterTextPaint.setTextAlign(Paint.Align.CENTER);
mClusterTextPaint.setTextSize(30);
mClusterTextPaint.setFakeBoldText(true);
}
mInternalItemList = new ArrayList();
}
/**
* Draw a marker on each of our items. populate() must have been called first.
*
* The marker will be drawn twice for each Item in the Overlay--once in the shadow phase,
* skewed
* and darkened, then again in the non-shadow phase. The bottom-center of the marker will be
* aligned with the geographical coordinates of the Item.
*
* The order of drawing may be changed by overriding the getIndexToDraw(int) method. An item
* may
* provide an alternate marker via its Marker.getMarker(int) method. If that method returns
* null, the default marker is used.
*
* The focused item is always drawn last, which puts it visually on top of the other
* items.
*
* @param canvas the Canvas upon which to draw. Note that this may already have a
* transformation
* applied, so be sure to leave it the way you found it
* @param mapView the MapView that requested the draw. Use MapView.getProjection() to convert
* between on-screen pixels and latitude/longitude pairs
* @param shadow if true, draw the shadow layer. If false, draw the overlay contents.
*/
@Override
protected void drawSafe(ISafeCanvas canvas, MapView mapView, boolean shadow) {
if (shadow) {
return;
}
if (mPendingFocusChangedEvent && mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChanged(this, mFocusedItem);
}
mPendingFocusChangedEvent = false;
final Projection pj = mapView.getProjection();
final int size = this.mInternalItemList.size() - 1;
final RectF bounds =
new RectF(0, 0, mapView.getMeasuredWidth(), mapView.getMeasuredHeight());
pj.rotateRect(bounds);
final float mapScale = 1 / mapView.getScale();
/* Draw in backward cycle, so the items with the least index are on the front. */
for (int i = size; i >= 0; i--) {
final Marker item = getItem(i);
if (item == mFocusedItem) {
continue;
}
onDrawItem(canvas, item, pj, mapView.getMapOrientation(), bounds, mapScale);
}
if (mFocusedItem != null) {
onDrawItem(canvas, mFocusedItem, pj, mapView.getMapOrientation(), bounds, mapScale);
}
}
/**
* Utility method to perform all processing on a new ItemizedOverlay. Subclasses provide Items
* through the createItem(int) method. The subclass should call this as soon as it has data,
* before anything else gets called.
*/
protected void populate() {
final int size = size();
mInternalItemList.clear();
mInternalItemList.ensureCapacity(size);
for (int a = 0; a < size; a++) {
mInternalItemList.add(createItem(a));
}
}
/**
* Returns the Item at the given index.
*
* @param position the position of the item to return
* @return the Item of the given index.
*/
public final Marker getItem(final int position) {
return mInternalItemList.get(position);
}
/**
* Draws an item located at the provided screen coordinates to the canvas.
*
* @param canvas what the item is drawn upon.
* @param item the item to be drawn.
* @param projection the projection to use.
* @param aMapOrientation
* @param mapBounds
* @param mapScale
*/
protected void onDrawItem(ISafeCanvas canvas, final Marker item, final Projection projection,
final float aMapOrientation, final RectF mapBounds, final float mapScale) {
item.updateDrawingPosition();
final PointF position = item.getPositionOnMap();
final Point roundedCoords = new Point((int) position.x, (int) position.y);
if (!RectF.intersects(mapBounds, item.getDrawingBounds(projection, null))) {
//dont draw item if offscreen
return;
}
canvas.save();
canvas.scale(mapScale, mapScale, position.x, position.y);
final int state =
(mDrawFocusedItem && (mFocusedItem == item) ? Marker.ITEM_STATE_FOCUSED_MASK : 0);
final Drawable marker = item.getMarker(state);
if (marker == null) {
return;
}
final Point point = item.getAnchor();
// draw it
if (this.isUsingSafeCanvas()) {
Overlay.drawAt(canvas.getSafeCanvas(), marker, roundedCoords, point, false,
aMapOrientation);
} else {
canvas.getUnsafeCanvas(new UnsafeCanvasHandler() {
@Override
public void onUnsafeCanvas(Canvas canvas) {
Overlay.drawAt(canvas, marker, roundedCoords, point, false, aMapOrientation);
}
});
}
canvas.restore();
}
protected boolean markerHitTest(final Marker pMarker, final Projection pProjection,
final float pX, final float pY) {
RectF rect = pMarker.getDrawingBounds(pProjection, null);
rect.bottom -=
rect.height() / 2; //a marker drawing bounds is twice the actual size of the marker
return rect.contains(pX, pY);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e, MapView mapView) {
final int size = this.size();
final Projection projection = mapView.getProjection();
final float x = e.getX();
final float y = e.getY();
for (int i = 0; i < size; i++) {
final Marker item = getItem(i);
if (markerHitTest(item, projection, x, y)) {
// We have a hit, do we get a response from onTap?
if (onTap(i)) {
// We got a response so consume the event
return true;
}
}
}
return super.onSingleTapConfirmed(e, mapView);
}
/**
* Override this method to handle a "tap" on an item. This could be from a touchscreen tap on
* an
* onscreen Item, or from a trackball click on a centered, selected Item. By default, does
* nothing and returns false.
*
* @return true if you handled the tap, false if you want the event that generated it to pass to
* other overlays.
*/
protected boolean onTap(int index) {
return false;
}
/**
* Set whether or not to draw the focused item. The default is to draw it, but some clients may
* prefer to draw the focused item themselves.
*/
public void setDrawFocusedItem(final boolean drawFocusedItem) {
mDrawFocusedItem = drawFocusedItem;
}
/**
* If the given Item is found in the overlay, force it to be the current focus-bearer. Any
* registered {@link ItemizedOverlay} will be notified. This does not move
* the map, so if the Item isn't already centered, the user may get confused. If the Item is
* not
* found, this is a no-op. You can also pass null to remove focus.
*/
public void setFocus(final Marker item) {
mPendingFocusChangedEvent = item != mFocusedItem;
mFocusedItem = item;
}
/**
* @return the currently-focused item, or null if no item is currently focused.
*/
public Marker getFocus() {
return mFocusedItem;
}
/**
* an item want's to be blured, if it is the focused one, blur it
*/
public void blurItem(final Marker item) {
if (mFocusedItem == item) {
setFocus(null);
}
}
// /**
// * Adjusts a drawable's bounds so that (0,0) is a pixel in the location described by the anchor
// * parameter. Useful for "pin"-like graphics. For convenience, returns the same drawable that
// * was passed in.
// *
// * @param marker the drawable to adjust
// * @param anchor the anchor for the drawable (float between 0 and 1)
// * @return the same drawable that was passed in.
// */
// protected synchronized Drawable boundToHotspot(final Drawable marker, Point anchor) {
// final int markerWidth = marker.getIntrinsicWidth();
// final int markerHeight = marker.getIntrinsicHeight();
//
// mRect.set(0, 0, markerWidth, markerHeight);
// mRect.offset(anchor.x, anchor.y);
// marker.setBounds(mRect);
// return marker;
// }
public void setOnFocusChangeListener(OnFocusChangeListener l) {
mOnFocusChangeListener = l;
}
public static interface OnFocusChangeListener {
void onFocusChanged(ItemizedOverlay overlay, Marker newFocus);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy