2011-08-22 13 views
2

我想實現將引腳動畫放置到標記中作爲iPhone中的標記。問題是引腳沒有按照他們在這個鏈接中的方式放下: http://googlegeodevelopers.blogspot.com/2010/12/map-markers-they-move.html 。 我已經添加了我的示例的代碼。 請幫助實施動畫。如何在android中使用谷歌地圖實現Drop pin動畫?

public class MyMapAnimation extends MapActivity { 
private MapView map = null; 
private MyLocationOverlay me = null; 
ImageView imageView = null; 
Projection proj = null; 
private Drawable marker; 
private ArrayList<OverlayItem> itemsArrayList; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    Log.i("NooYawk","onCreate"); 

    itemsArrayList = new ArrayList<OverlayItem>(); 
    itemsArrayList.add(new OverlayItem(getPoint(40.748963847316034, 
      -73.96807193756104), "UN", "United Nations")); 
    itemsArrayList.add(new OverlayItem(getPoint(40.76866299974387, 
      -73.98268461227417), "Lincoln Center", 
      "Home of Jazz at Lincoln Center")); 

    itemsArrayList.add(new OverlayItem(getPoint(40.765136435316755, 
      -73.97989511489868), "Carnegie Hall", 
      "Where you go with practice, practice, practice")); 
    itemsArrayList.add(new OverlayItem(getPoint(40.70686417491799, 
      -74.01572942733765), "The Downtown Club", 
      "Original home of the Heisman Trophy")); 

    map = (MapView) findViewById(R.id.map); 


    map.getController().setCenter(getPoint(40.748963847316034, 
    -73.96807193756104)); 
/* map.getController().setCenter(
      getPoint(40.748963847316034, -73.96807193756104));*/ 
    map.getController().setZoom(12); 
    map.setBuiltInZoomControls(true); 

    marker = getResources().getDrawable(R.drawable.marker); 

    proj = map.getProjection(); 
    imageView = new ImageView(this); 

    imageView.setBackgroundResource(R.drawable.marker); 

    marker.setBounds(0, 0, marker.getIntrinsicWidth(), 
      marker.getIntrinsicHeight()); 


    map.getOverlays().add(new SitesOverlay(marker)); 

    me = new MyLocationOverlay(this, map); 
    map.getOverlays().add(me); 

    /* 
    * map.getController().animateTo(getPoint(40.748963847316034, 
    * -73.96807193756104)); 
    */ 

} 

@Override 
public void onResume() { 
    super.onResume(); 
    me.enableCompass(); 
    Log.i("NooYawk","onResume"); 
} 

@Override 
public void onPause() { 
    super.onPause(); 
    Log.i("NooYawk","onPause"); 
    me.disableCompass(); 
} 

@Override 
protected boolean isRouteDisplayed() { 
    Log.i("NooYawk","isRouteDisplayed"); 
    return (false); 
} 

@Override 
public boolean onKeyDown(int keyCode, KeyEvent event) { 
    if (keyCode == KeyEvent.KEYCODE_S) { 
     map.setSatellite(!map.isSatellite()); 
     return (true); 
    } else if (keyCode == KeyEvent.KEYCODE_Z) { 
     map.displayZoomControls(true); 
     return (true); 
    } 

    return (super.onKeyDown(keyCode, event)); 
} 

private GeoPoint getPoint(double lat, double lon) { 
    Log.i("NooYawk","getPoint"); 
    return (new GeoPoint((int) (lat * 1000000.0), (int) (lon * 1000000.0))); 
} 

private class SitesOverlay extends ItemizedOverlay<OverlayItem> { 
    private List<OverlayItem> items = new ArrayList<OverlayItem>(); 
    private Drawable marker = null; 
    private OverlayItem inDrag = null; 
    private ImageView dragImage = null; 
    private int xDragImageOffset = 0; 
    private int yDragImageOffset = 0; 
    private int xDragTouchOffset = 0; 
    private int yDragTouchOffset = 0; 
    Point p = new Point(0, 0); 
    private RelativeLayout relativeLayout; 
    private ArrayList<ImageView> imageViewArrayList; 

    public SitesOverlay(Drawable marker) { 
     super(marker); 
     this.marker = marker; 



     Log.i("NooYawk","SitesOverlay"); 
dragImage = (ImageView) findViewById(R.id.drag); 



     Log.d("POint", 
       "" 
         + (map.getProjection().toPixels(
           getPoint(40.748963847316034, 
             -73.96807193756104), p).x) 
         + "," 
         + (map.getProjection().toPixels(
           getPoint(40.748963847316034, 
             -73.96807193756104), p).y)); 

     TranslateAnimation translateAnimation = null; 

     imageViewArrayList = new ArrayList<ImageView>(); 
     for (OverlayItem currentOverlayItem : itemsArrayList) { 
      Log.i("NooYawk","currentOverlayItem"); 
      imageView = new ImageView(NooYawk.this); 

      imageView.setBackgroundResource(R.drawable.marker); 

      relativeLayout = (RelativeLayout) findViewById(R.id.rl_map_main); 
      RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
      param.leftMargin = getWindowManager().getDefaultDisplay() 
        .getWidth()/2; 
      param.topMargin = 0; 

      relativeLayout.addView(imageView, param); 
      imageViewArrayList.add(imageView); 
     } 
     int count = 0; 
     Log.i("x for translate before animation", 
       (map.getProjection().toPixels(
         itemsArrayList.get(0).getPoint(), p).x)+""); 
     for (ImageView currentImageView : imageViewArrayList) { 
      Log.i("NooYawk","currentImageView"); 
      translateAnimation = new TranslateAnimation((map 
        .getProjection().toPixels(
          itemsArrayList.get(count).getPoint(), p).x), 
        (map.getProjection().toPixels(
          itemsArrayList.get(count).getPoint(), p).x), 0, 
        (map.getProjection().toPixels(
          itemsArrayList.get(count).getPoint(), p).y)); 


      translateAnimation.setDuration(2000); 
      currentImageView.startAnimation(translateAnimation); 
      count++; 
     } 

     new Handler().postDelayed(new Runnable() { 


      @Override 
      public void run() { 
       Log.i("NooYawk","run"); 
       // relativeLayout.removeView(imageView); 
       for (ImageView currentImageView : imageViewArrayList) { 
        Log.i("NooYawk","thread : currentImageView"); 
        currentImageView.setVisibility(View.INVISIBLE); 
       } 
       setItemValues(); 
       populate(); 

       Log.i("x for translate after animation", 
         (map.getProjection().toPixels(
           itemsArrayList.get(0).getPoint(), p).x)+""); 
      } 
     }, translateAnimation.getDuration()); 

    } 

    private void setItemValues() { 
     int count = 0; 
     Log.i("NooYawk","setItemVAlues"); 
     for (OverlayItem currentOverlayItem : itemsArrayList) { 
      items.add(currentOverlayItem); 

      Log.i("items ", items.get(count).getPoint()+""); 
      count++; 
     } 
    } 

    @Override 
    protected OverlayItem createItem(int i) { 
     Log.i("NooYawk","createitem"); 
     return (items.get(i)); 
    } 

    @Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 
     super.draw(canvas, mapView, shadow); 
     // items.get(0) 
     Log.i("NooYawk","draw"); 
     boundCenterBottom(marker); 

    } 

    @Override 
    public int size() { 
     Log.i("NooYawk","size"); 
     return (items.size()); 
    } 

} 

}

+2

問題是否解決? 如果是,那麼你是怎麼做到的? 您可以請分享,因爲我也在尋找類似的動畫類型。 是否可以使用JavaScript開發這種類型的動畫,以及我們如何在本機Android應用程序中使用JavaScript? – 2011-10-15 09:46:51

+0

按照這個鏈接... http://stackoverflow.com/questions/10607042/tutorial-android-map-pin-drop-animation-working-right –

+0

我已經使用斯科特的答案,它做得很好 – kozyr

回答

2

我張貼的工作有在圖形頁面下落銷動畫作爲iPhopne的核心理念。 同樣可以用疊加也可以集成....

Drawable drawableImage = this.getResources().getDrawable(R.drawable.marker); 
myCustomOverlay = new CustomOverlay(drawableImage, mapView); 
initGeoPoint = new GeoPoint((int) (latitudeArray[i] * 1E6),(int) (longitudeArray[i] * 1E6)); 
OverlayItem overlayItem = new OverlayItem(initGeoPoint,getLocationAddress(latitudeArray[i], longitudeArray[i]),"xyz"); 

myCustomOverlay.addOverlay(overlayItem); 
mapOverlays.add(myCustomOverlay); 

RelativeLayout v = (RelativeLayout) View.inflate(getApplicationContext(),R.layout.marker_layout, null); 
ImageView markerView = (ImageView) v.findViewById(R.id.marker_img_view); 
AnimationSet animation = new AnimationSet(true); 

TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f, -400.0f, 0.0f); 
translateAnimation.setDuration(1000); 
animation.addAnimation(translateAnimation); 

markerView.startAnimation(animation); 

mapView.addView(v, 
     new MapView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT), 
     new GeoPoint((int) (latitude * 1E6),(int) (longitude * 1E6)), 
     MapView.LayoutParams.BOTTOM_CENTER)); 

mapView.invalidate(); 

marker_layout是folows:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" > 

    <ImageView 
     android:id="@+id/marker_img_view" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentBottom="true" 
     android:layout_centerHorizontal="true" 
     android:src="@drawable/marker" /> 

</RelativeLayout> 

疊加類###########

public class CustomOverlay extends BalloonItemizedOverlay<OverlayItem> { 

    private ArrayList<OverlayItem> m_overlays = new ArrayList<OverlayItem>(); 
    private Context c; 

    public CustomOverlay(Drawable defaultMarker, MapView mapView) { 
     super(boundCenter(defaultMarker), mapView); 
     c = mapView.getContext(); 
    } 

    public void addOverlay(OverlayItem overlay) { 
     m_overlays.add(overlay); 
     populate(); 
    } 

    @Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 
     if (!shadow) { 
      super.draw(canvas, mapView, false); 
     } 
    } 

    public void removeOverlay(OverlayItem overlay) { 
     m_overlays.remove(overlay); 
     populate(); 
    } 

    @Override 
    protected OverlayItem createItem(int i) { 
     return m_overlays.get(i); 
    } 

    @Override 
    public int size() { 
     return m_overlays.size(); 
    } 

    @Override 
    protected boolean onBalloonTap(int index, OverlayItem item) { 
     Toast.makeText(
       c, 
       "onBalloonTap for overlay index " + index + " Item" 
         + item.getTitle(), Toast.LENGTH_LONG).show(); 

     String id = ""; 

     return true; 
    } 
} 
+0

任何關於如何將這個與Overlays整合的例子?據我所知,疊加層不是視圖,而是對象。 –

+0

您可以將疊加層添加到地理位置。此外,您還可以在放置動畫完成時顯示疊加層...只是爲了使用pin動畫我們已經使用視圖..但唯一的將是疊加本身。這些都沒有顯示在代碼 –

+0

我編輯了理解代碼 –

1

在MapView上動畫項目的最佳方式是不使用視圖,而是使用疊加層的動畫效果。

這是我製作的一個類,用於處理動畫地圖標記以複製您在iPhone上獲得的良好效果的行爲。在這種情況下,我添加了標記來指示企業,因此它在「商業」這個字眼中指的是一個標記。此代碼尚未在許多設備上進行測試。我知道投影類可能會出現一些問題,可能會導致問題,因此不要將此代碼視爲穩定並進行相應處理。

import java.util.ArrayList; 

import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Point; 
import android.os.AsyncTask; 
import android.widget.AdapterView.OnItemClickListener; 

import com.google.android.maps.GeoPoint; 
import com.google.android.maps.MapView; 
import com.google.android.maps.Overlay; 
import com.google.android.maps.Projection; 

public class MarkerOverlay extends Overlay { 
    private ArrayList<Business> mOverlays = new ArrayList<Business>(); 
    private Bitmap mBitmapMarker; 
    private Bitmap mBitmapShadow; 

    DropMarkersTask animateMarkers; 
    OnItemClickListener itemClickedListener; 

    public MarkerOverlay(Bitmap defaultMarker) { 
     mBitmapMarker = defaultMarker; 

     // Create shadow bitmap. Basically a black version of the image 
     mBitmapShadow = defaultMarker.copy(Bitmap.Config.ARGB_8888, true); 
     for(int x = 0;x < mBitmapShadow.getWidth();x++) 
      for(int y = 0;y < mBitmapShadow.getHeight();y++) 
       if(mBitmapShadow.getPixel(x, y) != Color.TRANSPARENT) // This is a little lazy but it works 
        mBitmapShadow.setPixel(x, y, Color.BLACK); 
    } 

    public void setOnItemClickListener(OnItemClickListener clickListener) { 
     this.itemClickedListener = clickListener; 
    } 

    public void addBusiness(Business overlay) { 
     for(Business business : mOverlays) { 
      if(overlay.getPoint().getLatitudeE6() == business.getPoint().getLatitudeE6() && 
        overlay.getPoint().getLongitudeE6() == overlay.getPoint().getLongitudeE6()) { 
       // Don't add any markers which exist at exactly the same location, chances are it's the same marker 
       return; 
      } else if (overlay.getPoint().getLatitudeE6() > business.getPoint().getLatitudeE6()) { 
       // This is so all the markers are listed top to bottom 
       mOverlays.add(mOverlays.indexOf(business), overlay); 
       return; 
      } 
     } 
     mOverlays.add(overlay); 
    } 

    public Business getItem(int position) { 
     return mOverlays.get(position); 
    } 

    @Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 

     super.draw(canvas, mapView, shadow); 
     Projection projection = mapView.getProjection(); 
     boolean animationRequired = false; 

     // Find the bounds in which the markers must reside to be displayed 
     GeoPoint bottomLeft = projection.fromPixels(- mBitmapMarker.getWidth()/2, mapView.getHeight() + mBitmapMarker.getHeight()); 
     GeoPoint topRight = projection.fromPixels(mapView.getWidth() + mBitmapMarker.getWidth()/2, 0); 

     for(Business business : mOverlays) { 
      // Check to ensure the marker is inside the bounds 
      if(business.getPoint().getLatitudeE6() > bottomLeft.getLatitudeE6() && business.getPoint().getLatitudeE6() < topRight.getLatitudeE6() 
        && business.getPoint().getLongitudeE6() > bottomLeft.getLongitudeE6() && business.getPoint().getLongitudeE6() < topRight.getLongitudeE6()) { 
       if(business.isNewPoint()) { 
        business.setOffset(mapView.getHeight()); 
        business.setOldPoint(); 
       } 
       Point pt = new Point(); 
       projection.toPixels(business.getPoint() ,pt); 

       if(shadow) { 
        // Set the location of the shadow according to the offset so it appears to come in from the top right 
        pt.x = pt.x + (mBitmapMarker.getWidth()/4) + ((int)business.getOffset()/2); 
        pt.y = pt.y - (mBitmapMarker.getHeight()/2) - ((int)business.getOffset()/2); 

        // Skew the shadow and set the location 
        Matrix matrix = new Matrix(); 
        matrix.preSkew(-0.8f, 0f); 
        matrix.preScale(1f, 0.5f); 
        matrix.postTranslate(pt.x, pt.y); 

        // Change transparency according to the offset 
        Paint paint = new Paint(); 
        paint.setAlpha((int)(((mapView.getHeight() - business.getOffset())/mapView.getHeight()) * 100)); 

        // Draw it 
        canvas.drawBitmap(mBitmapShadow, matrix, paint); 
       } else { 
        // Set the position according to the offset 
        pt.x = pt.x - (mBitmapMarker.getWidth()/2); 
        pt.y = pt.y - mBitmapMarker.getHeight() - (int)business.getOffset(); 

        canvas.drawBitmap(mBitmapMarker, (float)pt.x, (float)pt.y, null); 

        if(business.getOffset() > 0) { 
         animationRequired = true; 
        } 
       } 
      } 
     } 

     // Start the animation task if it hasn't already been started 
     if(animationRequired && (animateMarkers == null || animateMarkers.getStatus() != AsyncTask.Status.RUNNING)) { 
      animateMarkers = new DropMarkersTask(); 
      animateMarkers.execute(mapView); 
     } 
    } 

    @Override 
    public boolean onTap(GeoPoint point, MapView map) { 
     if(itemClickedListener == null) { 
      return false; 
     } 

     Projection projection = map.getProjection(); 
     int imageWidth = mBitmapMarker.getWidth(); 
     int imageHeight = mBitmapMarker.getHeight(); 

     // Find the point on the screen which has been clicked 
     Point clickPoint = new Point(); 
     projection.toPixels(point, clickPoint); 

     // Go backwards through the businesses and find out if the location falls within their marker 
     for(int i = mOverlays.size() - 1; i >= 0; i--) { 
      Business business = mOverlays.get(i); 
      Point businessPoint = new Point(); 
      projection.toPixels(business.getPoint(), businessPoint); 
      if(businessPoint.x > 0 && businessPoint.x < map.getWidth() && 
        businessPoint.y > 0 && businessPoint.y < map.getHeight()) { 
       // Point is visible, so may clicked 
       int left = businessPoint.x - (imageWidth/2); 
       int right = businessPoint.x + (imageWidth/2); 
       int top = businessPoint.y - imageHeight; 
       int bottom = businessPoint.y; 

       if(clickPoint.x >= left && clickPoint.x <= right && clickPoint.y >= top && clickPoint.y <= bottom) { // Item has been clicked     
        // Adapter will be null as this isn't one. We will return the map 
        // in the view for consistency but most importantly the index of the item 
        itemClickedListener.onItemClick(null, map, i, 0); 
        return true; 
       } 
      } 

     } 

     return false; 
    } 

    class DropMarkersTask extends AsyncTask<MapView, Void, Void> { 
     MapView mapView; 

     @Override 
     protected Void doInBackground(MapView... mapViews) { 
      mapView = mapViews[0]; 
      boolean mapUpdate = true; 

      try { 
       while(mapUpdate) { 
        Projection projection = mapView.getProjection(); 
        GeoPoint bottomLeft = projection.fromPixels(- mBitmapMarker.getWidth()/2, mapView.getHeight() + mBitmapMarker.getHeight()); 
        GeoPoint topRight = projection.fromPixels(mapView.getWidth() + mBitmapMarker.getWidth()/2, 0); 
        mapUpdate = false; 

        // Any visible markers with an offset higher than zero must be falling and therefore must be moved. 
        for(Business business : mOverlays) { 
         if(business.getPoint().getLatitudeE6() > bottomLeft.getLatitudeE6() && business.getPoint().getLatitudeE6() < topRight.getLatitudeE6() 
           && business.getPoint().getLongitudeE6() > bottomLeft.getLongitudeE6() && business.getPoint().getLongitudeE6() < topRight.getLongitudeE6()) { 
          if(business.getOffset() > 0) { 
           // A nice Quadratic fall curve. 
           double currentY = Math.sqrt(mapView.getHeight() - business.getOffset()); 
           currentY = currentY + 0.5; 
           double dropDistance = Math.pow(currentY, 2); 
           double newOffset = mapView.getHeight() - dropDistance; 
           if(newOffset < 0) { // Marker can't have an offset less than zero 
            newOffset = 0; 
           } 
           business.setOffset(newOffset); 
           mapUpdate = true; 
          } 
         } 
        } 

        if(mapUpdate) { 
         this.publishProgress(); 
         Thread.sleep(20); 
        } 
       } 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

      return null; 
     } 

     @Override 
     protected void onProgressUpdate(Void... unused) { 
      mapView.postInvalidate(); 
     } 
    } 
} 

設置覆蓋(最有可能在你的onCreate方法):

mapBusinesses = (EventMapView)findViewById(R.id.mapBusinesses); 

    mapOverlays = mapBusinesses.getOverlays(); 

    Bitmap drawable = BitmapFactory.decodeResource(this.getResources(), R.drawable.map_pin); 
    businessMarkerOverlay = new MarkerOverlay(drawable); 
    businessMarkerOverlay.setOnItemClickListener(new OnItemClickListener() { 
     @Override 
     public void onItemClick(AdapterView<?> adapter, View view, int position, long id) { 
      Business business = businessMarkerOverlay.getItem(position); 
      AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.class); 
      dialog.setTitle(business.getBusiness()); 
      dialog.show(); 
     } 
    }); 
    mapOverlays.add(businessMarkerOverlay); 

添加一個新的標誌: 不要忘記調用postInvalidate在地圖上揭開序幕,當第一次抽籤它映射它否則空閒。

businessMarkerOverlay.addBusiness(business); 
mapBusinesses.postInvalidate(); 

最後,這是我使用的業務對象的類:

public class Business { 
    private String _business; 
    private int _businessId; 
    private GeoPoint _point; 

    private boolean _newPoint; 
    private double _offset; 

    public Business(GeoPoint point, String business, int businessId) { 
     _point = point; 
     _business = business; 
     _businessId = businessId; 
     _newPoint = true; 
    } 

    public int getBusinessId() { return _businessId; } 
    public String getBusiness() { return _business; } 
    public GeoPoint getPoint() { return _point; } 
    public boolean isNewPoint() { return _newPoint; } 
    public double getOffset() { return _offset; } 

    public void setOldPoint() { _newPoint = false; } 
    public void setOffset(double offset) { _offset = offset; } 
} 
+1

你應該使用像素來檢查邊界。不是緯/長。看到我的答案。 –

1

斯科特。我喜歡你的榜樣,但你的界限計算有點偏離。

下面是它應該如何。

import java.util.ArrayList; 

import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Point; 
import android.graphics.Rect; 
import android.os.AsyncTask; 
import android.widget.AdapterView.OnItemClickListener; 

import com.google.android.maps.GeoPoint; 
import com.google.android.maps.MapView; 
import com.google.android.maps.Overlay; 
import com.google.android.maps.Projection; 

public class MarkerOverlay extends Overlay { 
    private ArrayList<Business> mOverlays = new ArrayList<Business>(); 
    private Bitmap mBitmapMarker; 
    private Bitmap mBitmapShadow; 

    DropMarkersTask animateMarkers; 
    OnItemClickListener itemClickedListener; 

    public MarkerOverlay(Bitmap defaultMarker) { 
     mBitmapMarker = defaultMarker; 

     // Create shadow bitmap. Basically a black version of the image 
     mBitmapShadow = defaultMarker.copy(Bitmap.Config.ARGB_8888, true); 
     for(int x = 0;x < mBitmapShadow.getWidth();x++) 
      for(int y = 0;y < mBitmapShadow.getHeight();y++) 
       if(mBitmapShadow.getPixel(x, y) != Color.TRANSPARENT) // This is a little lazy but it works 
        mBitmapShadow.setPixel(x, y, Color.BLACK); 
    } 

    public void setOnItemClickListener(OnItemClickListener clickListener) { 
     this.itemClickedListener = clickListener; 
    } 

    public void addBusiness(Business overlay) { 
     for(Business business : mOverlays) { 
      if(overlay.getPoint().getLatitudeE6() == business.getPoint().getLatitudeE6() && 
        overlay.getPoint().getLongitudeE6() == overlay.getPoint().getLongitudeE6()) { 
       // Don't add any markers which exist at exactly the same location, chances are it's the same marker 
       return; 
      } else if (overlay.getPoint().getLatitudeE6() > business.getPoint().getLatitudeE6()) { 
       // This is so all the markers are listed top to bottom 
       mOverlays.add(mOverlays.indexOf(business), overlay); 
       return; 
      } 
     } 
     mOverlays.add(overlay); 
    } 

    public Business getItem(int position) { 
     return mOverlays.get(position); 
    } 

    public static Rect getMapDrawingRect(MapView mapView, Bitmap marker) { 
     Rect mapDrawRect = new Rect(); 
     mapView.getDrawingRect(mapDrawRect); 
     mapDrawRect.left = mapDrawRect.left - marker.getWidth(); // full marker width to include shadow 
     mapDrawRect.right = mapDrawRect.right + marker.getWidth()/2; 
     mapDrawRect.bottom = mapDrawRect.bottom + marker.getHeight(); // full height of marker 
     return mapDrawRect; 
    } 

    @Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 
     super.draw(canvas, mapView, shadow); 

     Projection projection = mapView.getProjection(); 
     boolean animationRequired = false; 

     Rect mapDrawRect = getMapDrawingRect(mapView, mBitmapMarker); 
     for(Business business : mOverlays) { 
      Point outPoint = new Point(); 
      projection.toPixels(business.getPoint(), outPoint); 
      if (!mapDrawRect.contains(outPoint.x, outPoint.y)) { 
       continue; 
      } 

      if(business.isNewPoint()) { 
       business.setOffset(mapView.getHeight()); 
       business.setOldPoint(); 
      } 
      Point pt = new Point(); 
      projection.toPixels(business.getPoint() ,pt); 

      if(shadow) { 
       // Set the location of the shadow according to the offset so it appears to come in from the top right 
       pt.x = pt.x + (mBitmapMarker.getWidth()/4) + ((int)business.getOffset()/2); 
       pt.y = pt.y - (mBitmapMarker.getHeight()/2) - ((int)business.getOffset()/2); 

       // Skew the shadow and set the location 
       Matrix matrix = new Matrix(); 
       matrix.preSkew(-0.8f, 0f); 
       matrix.preScale(1f, 0.5f); 
       matrix.postTranslate(pt.x, pt.y); 

       // Change transparency according to the offset 
       Paint paint = new Paint(); 
       paint.setAlpha((int)(((mapView.getHeight() - business.getOffset())/mapView.getHeight()) * 100)); 

       // Draw it 
       canvas.drawBitmap(mBitmapShadow, matrix, paint); 
      } else { 
       // Set the position according to the offset 
       pt.x = pt.x - (mBitmapMarker.getWidth()/2); 
       pt.y = pt.y - mBitmapMarker.getHeight() - (int)business.getOffset(); 

       canvas.drawBitmap(mBitmapMarker, (float)pt.x, (float)pt.y, null); 
       if(business.getOffset() > 0) { 
        animationRequired = true; 
       } 
      } 
     } 

     // Start the animation task if it hasn't already been started 
     if(animationRequired && (animateMarkers == null || animateMarkers.getStatus() != AsyncTask.Status.RUNNING)) { 
      animateMarkers = new DropMarkersTask(); 
      animateMarkers.execute(mapView); 
     } 
    } 

    @Override 
    public boolean onTap(GeoPoint point, MapView map) { 
     if(itemClickedListener == null) { 
      return false; 
     } 

     Projection projection = map.getProjection(); 
     int imageWidth = mBitmapMarker.getWidth(); 
     int imageHeight = mBitmapMarker.getHeight(); 

     // Find the point on the screen which has been clicked 
     Point clickPoint = new Point(); 
     projection.toPixels(point, clickPoint); 

     // Go backwards through the businesses and find out if the location falls within their marker 
     for(int i = mOverlays.size() - 1; i >= 0; i--) { 
      Business business = mOverlays.get(i); 
      Point businessPoint = new Point(); 
      projection.toPixels(business.getPoint(), businessPoint); 
      if(businessPoint.x > 0 && businessPoint.x < map.getWidth() && 
        businessPoint.y > 0 && businessPoint.y < map.getHeight()) { 
       // Point is visible, so may clicked 
       int left = businessPoint.x - (imageWidth/2); 
       int right = businessPoint.x + (imageWidth/2); 
       int top = businessPoint.y - imageHeight; 
       int bottom = businessPoint.y; 

       if(clickPoint.x >= left && clickPoint.x <= right && clickPoint.y >= top && clickPoint.y <= bottom) { // Item has been clicked     
        // Adapter will be null as this isn't one. We will return the map 
        // in the view for consistency but most importantly the index of the item 
        itemClickedListener.onItemClick(null, map, i, 0); 
        return true; 
       } 
      } 

     } 

     return false; 
    } 

    class DropMarkersTask extends AsyncTask<MapView, Void, Void> { 
     MapView mapView; 

     @Override 
     protected Void doInBackground(MapView... mapViews) { 
      mapView = mapViews[0]; 
      boolean mapUpdate = true; 

      try { 
       while(mapUpdate) { 
        Projection projection = mapView.getProjection(); 
        Rect mapDrawRect = getMapDrawingRect(mapView, mBitmapMarker); 
        mapUpdate = false; 

        // Any visible markers with an offset higher than zero must be falling and therefore must be moved. 
        for(Business business : mOverlays) { 
         Point outPoint = new Point(); 
         projection.toPixels(business.getPoint(), outPoint); 
         if (!mapDrawRect.contains(outPoint.x, outPoint.y)) { 
          continue; 
         } 

         if(business.getOffset() > 0) { 
          // A nice Quadratic fall curve. 
          double currentY = Math.sqrt(mapView.getHeight() - business.getOffset()); 
          currentY = currentY + 0.5; 
          double dropDistance = Math.pow(currentY, 2); 
          double newOffset = mapView.getHeight() - dropDistance; 
          if(newOffset < 0) { // Marker can't have an offset less than zero 
           newOffset = 0; 
          } 
          business.setOffset(newOffset); 
          mapUpdate = true; 
         } 
        } 

        if(mapUpdate) { 
         this.publishProgress(); 
         Thread.sleep(10); 
        } 
       } 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

      return null; 
     } 

     @Override 
     protected void onProgressUpdate(Void... unused) { 
      mapView.postInvalidate(); 
     } 
    } 
}