package com.mapbox.mapboxsdk.overlay; import android.content.Context; import android.graphics.Point; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.WindowManager; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.views.MapView; import com.mapbox.mapboxsdk.views.util.Projection; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class ItemizedIconOverlay extends ItemizedOverlay { protected final List<Marker> mItemList; protected OnItemGestureListener<Marker> mOnItemGestureListener; private int mDrawnItemsLimit = Integer.MAX_VALUE; private MapView view; private Context context; public ItemizedIconOverlay(final Context pContext, final List<Marker> pList, final com.mapbox.mapboxsdk.overlay.ItemizedIconOverlay.OnItemGestureListener<Marker> pOnItemGestureListener) { this(pContext, pList, pOnItemGestureListener, false); } public ItemizedIconOverlay(final Context pContext, final List<Marker> pList, final com.mapbox.mapboxsdk.overlay.ItemizedIconOverlay.OnItemGestureListener<Marker> pOnItemGestureListener, boolean sortList) { super(); this.context = pContext; this.mItemList = pList; this.mOnItemGestureListener = pOnItemGestureListener; if (sortList) { sortListByLatitude(); } populate(); } /** * Sorts List of Marker by Latitude */ protected void sortListByLatitude() { Collections.sort(mItemList, new Comparator<Marker>() { public int compare(Marker a, Marker b) { return Double.valueOf(a.getPoint().getLatitude()).compareTo(b.getPoint().getLatitude()); } }); } @Override public boolean onSnapToItem(final int pX, final int pY, final Point pSnapPoint, final MapView pMapView) { // TODO Implement this! return false; } @Override protected Marker createItem(final int index) { return mItemList.get(index); } @Override public int size() { return Math.min(mItemList.size(), mDrawnItemsLimit); } public boolean addItem(final Marker item) { item.setParentHolder(this); final boolean result = mItemList.add(item); populate(); return result; } // public void addItem(final int location, final Marker item) { // item.setParentHolder(this); // mItemList.add(location, item); // populate(); // } /** * When a content sensitive action is performed the content item needs to be identified. This * method does that and then performs the assigned task on that item. * * @return true if event is handled false otherwise */ private boolean activateSelectedItems(final MotionEvent event, final MapView mapView, final ActiveItem task) { final Projection projection = mapView.getProjection(); final float x = event.getX(); final float y = event.getY(); for (int i = 0; i < size(); ++i) { final Marker item = getItem(i); if (markerHitTest(item, projection, x, y)) { if (task.run(i)) { this.setFocus(item); return true; } } } return false; } public boolean addItems(final List<Marker> items) { for (Object item : items) { if (item instanceof Marker) { ((Marker) item).setParentHolder(this); } } final boolean result = mItemList.addAll(items); populate(); return result; } public void removeAllItems() { removeAllItems(true); } public void removeAllItems(final boolean withPopulate) { for (Marker item : mItemList) { item.setParentHolder(null); } mItemList.clear(); if (withPopulate) { populate(); } } protected void onItemRemoved(final Marker item) { blurItem(item); item.setParentHolder(null); } public boolean removeItem(final Marker item) { final boolean result = mItemList.remove(item); if (getFocus() == item) { setFocus(null); } if (result) { onItemRemoved(item); } populate(); return result; } public void clearFocus() { setFocus(null); } public Marker removeItem(final int position) { final Marker item = mItemList.remove(position); if (item != null) { onItemRemoved(item); } populate(); return item; } public void removeItems(final List items) { for (Object item : items) { if (item instanceof Marker) { final boolean result = mItemList.remove(item); if (result) { onItemRemoved((Marker) item); } } } populate(); } /** * Each of these methods performs a item sensitive check. If the item is located its * corresponding method is called. The result of the call is returned. * <p/> * Helper methods are provided so that child classes may more easily override behavior without * resorting to overriding the ItemGestureListener methods. */ @Override public boolean onSingleTapConfirmed(final MotionEvent event, final MapView mapView) { return (activateSelectedItems(event, mapView, new ActiveItem() { @Override public boolean run(final int index) { final ItemizedIconOverlay that = ItemizedIconOverlay.this; if (that.mOnItemGestureListener == null) { return false; } return onSingleTapUpHelper(index, getItem(index), mapView); } })); } protected boolean onSingleTapUpHelper(final int index, final Marker item, final MapView mapView) { return this.mOnItemGestureListener.onItemSingleTapUp(index, item); } @Override public boolean onLongPress(final MotionEvent event, final MapView mapView) { return (activateSelectedItems(event, mapView, new ActiveItem() { @Override public boolean run(final int index) { final ItemizedIconOverlay that = ItemizedIconOverlay.this; if (that.mOnItemGestureListener == null) { return false; } return onLongPressHelper(index, getItem(index)); } })); } protected boolean onLongPressHelper(final int index, final Marker item) { return this.mOnItemGestureListener.onItemLongPress(index, item); } private double getThreshold() { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); return (double) dm.widthPixels; } private ArrayList<LatLng> getCoordinateList(List<Marker> list) { ArrayList<LatLng> theList = new ArrayList<LatLng>(); for (Marker element : list) { theList.add(element.getPoint()); } return theList; } private float screenX(Marker item) { return view.getProjection().toMapPixels(item.getPoint(), null).x; } private float screenY(Marker item) { return view.getProjection().toMapPixels(item.getPoint(), null).y; } public int getDrawnItemsLimit() { return this.mDrawnItemsLimit; } public void setDrawnItemsLimit(final int aLimit) { this.mDrawnItemsLimit = aLimit; } /** * When the item is touched one of these methods may be invoked depending on the type of touch. * Each of them returns true if the event was completely handled. */ public static interface OnItemGestureListener<T> { public boolean onItemSingleTapUp(final int index, final T item); public boolean onItemLongPress(final int index, final T item); } public static interface ActiveItem { public boolean run(final int aIndex); } }