Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The best way to animate items on the MapView is not using Views, but animating the overlay.</p> <p>Here's a class I made to handle animating map markers to replicate the behaviour of the nice effect you get on the iPhone. In this case I was adding markers to indicate businesses so where it says 'Business' it is just referring to a marker. This code hasn't been tested on many devices. I know there can be some issues with the projection class which might cause issues so don't take this code as stable and treat accordingly.</p> <pre><code>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&lt;Business&gt; mOverlays = new ArrayList&lt;Business&gt;(); 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 &lt; mBitmapShadow.getWidth();x++) for(int y = 0;y &lt; 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() &amp;&amp; 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() &gt; 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() &gt; bottomLeft.getLatitudeE6() &amp;&amp; business.getPoint().getLatitudeE6() &lt; topRight.getLatitudeE6() &amp;&amp; business.getPoint().getLongitudeE6() &gt; bottomLeft.getLongitudeE6() &amp;&amp; business.getPoint().getLongitudeE6() &lt; 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() &gt; 0) { animationRequired = true; } } } } // Start the animation task if it hasn't already been started if(animationRequired &amp;&amp; (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 &gt;= 0; i--) { Business business = mOverlays.get(i); Point businessPoint = new Point(); projection.toPixels(business.getPoint(), businessPoint); if(businessPoint.x &gt; 0 &amp;&amp; businessPoint.x &lt; map.getWidth() &amp;&amp; businessPoint.y &gt; 0 &amp;&amp; businessPoint.y &lt; 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 &gt;= left &amp;&amp; clickPoint.x &lt;= right &amp;&amp; clickPoint.y &gt;= top &amp;&amp; clickPoint.y &lt;= 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&lt;MapView, Void, Void&gt; { 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() &gt; bottomLeft.getLatitudeE6() &amp;&amp; business.getPoint().getLatitudeE6() &lt; topRight.getLatitudeE6() &amp;&amp; business.getPoint().getLongitudeE6() &gt; bottomLeft.getLongitudeE6() &amp;&amp; business.getPoint().getLongitudeE6() &lt; topRight.getLongitudeE6()) { if(business.getOffset() &gt; 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 &lt; 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(); } } } </code></pre> <p>Setting up the overlay (most likely in your onCreate method):</p> <pre><code> 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&lt;?&gt; 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); </code></pre> <p>Adding a new marker: Don't forget to call postInvalidate on the map to kick off the first draw when the map it otherwise idle.</p> <pre><code>businessMarkerOverlay.addBusiness(business); mapBusinesses.postInvalidate(); </code></pre> <p>Lastly, this is the class I used for the Business object:</p> <pre><code>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; } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload