Using the standard Google Maps markers on a map in your application might not be the thing you want. Instead, you would like to personalize it a bit using a custom icon, for example. But what if you want to draw that overlay from scratch, containing some dynamic information, instead of using a drawable or bitmap?
It actually is quite simple, you just need to override the draw method in the Overlay class and the onTap method if you want to do some actions resulting a tap.
It actually is quite simple, you just need to override the draw method in the Overlay class and the onTap method if you want to do some actions resulting a tap.
I commented the code. The way I’ve selected the different points to draw on is the result of trying, moving it a bit to the left, to the top, etc. so you shouldn’t focus on that.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Typeface;
import android.util.Log;
import be.emich.villo.R;
import be.emich.villo.Villo;
import be.emich.villo.messages.StationMessage;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
public class CustomOverlay extends Overlay {
protected Bitmap[] icons;
protected GeoPoint point;
protected StationMessage msg;
protected static Bitmap GREENBIKE=null;
protected static Bitmap REDBIKE=null;
protected static Bitmap GREENPARKING=null;
protected static Bitmap REDPARKING=null;
protected static Bitmap MASTERCARD=null;
protected static final int offsetX=18;
protected static final int offsetY=20;
protected int mapWidth;
protected int mapHeight;
protected Context ctx;
/**
* Constructor of the overlay. Takes in a stationmessage,
* a POJO containing all info about the overlay to draw.
* The context, needed to create the images and show a popup
* when the overlay is clicked. The height and width are needed
* so we know if drawing the overlay is necessary or not.
* If the overlay is outside of the map, we don't minde because
* we can't see it.
*/
public CustomOverlay(Context ctx,StationMessage msg,int mapWidth,int mapHeight){
this.point=new GeoPoint((int)(msg.getLatitude()*1000000),(int)(msg.getLongitude()*1000000));
this.msg=msg;
//If the bitmaps are not loaded yet, we load them and put them in
//a static context so another instance of this overlay doesn't have
//to load it.
if(GREENBIKE==null)GREENBIKE=BitmapFactory.decodeResource(ctx.getResources(), R.drawable.greenbikesmall);
if(REDBIKE==null)REDBIKE=BitmapFactory.decodeResource(ctx.getResources(), R.drawable.redbikesmall);
if(GREENPARKING==null)GREENPARKING=BitmapFactory.decodeResource(ctx.getResources(), R.drawable.parkinggreen);
if(REDPARKING==null)REDPARKING=BitmapFactory.decodeResource(ctx.getResources(), R.drawable.parkingred);
if(MASTERCARD==null)MASTERCARD=BitmapFactory.decodeResource(ctx.getResources(), R.drawable.mastercard);
//If ticket equals 1 we have to draw an extra icon.
if(msg.getTicket()==1){icons = new Bitmap[3];icons[2]=MASTERCARD;}
else icons = new Bitmap[2];
//We fill up the other icons here.
icons[0]=msg.getBikes()!=0?GREENBIKE:REDBIKE;
icons[1]=msg.getParking()!=0?GREENPARKING:REDPARKING;
this.mapWidth=mapWidth;
this.mapHeight=mapWidth;
this.ctx=ctx;
}
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
//Translate point to x y coordinates on the screen
Point pt = mapView.getProjection().toPixels(point, null);
//Is the overlay outside the viewing area? If yes, do not draw it
if(pt.x<-90 || pt.x>mapWidth || pt.y<-50 || pt.y>mapHeight+50)return;
//Define brush 1
Paint p1 = new Paint();
p1.setColor(Color.BLACK);
//Define brush 2
Paint p2 = new Paint();
p2.setColor(Color.WHITE);
//Define path
Path path = new Path();
//Corner top left
path.moveTo(offsetX+pt.x-20,offsetY+pt.y-60);
//Corner top right
path.lineTo(offsetX+pt.x+2+(18*(getCases()-1)),offsetY+pt.y-60);
//Corner bottom right
path.lineTo(offsetX+pt.x+2+(18*(getCases()-1)),offsetY+pt.y-30);
//Corner triangle right
path.lineTo(offsetX+pt.x-10,offsetY+pt.y-30);
//Bottom triangle middle
path.lineTo(offsetX+pt.x-20,offsetY+pt.y-20);
//Corner bottom left
path.lineTo(offsetX+pt.x-20,offsetY+pt.y-30);
//Corner top left
path.lineTo(offsetX+pt.x-20,offsetY+pt.y-60);
//Paint polygon (made with path) and fill it using brush 2
p2.setStyle(Paint.Style.FILL);
canvas.drawPath(path,p2);
//Yellow station name rect using brush 1
p1.setColor(Color.rgb(255, 193, 40));
p1.setStyle(Paint.Style.FILL);
//Draw yellow box
Path path2=new Path();
//Corner top left
path2.moveTo(offsetX+pt.x-20,offsetY+pt.y-60);
//Corner top right
path2.lineTo(offsetX+pt.x+2+(18*(getCases()-1)),offsetY+pt.y-60);
//Corner bottom right
path2.lineTo(offsetX+pt.x+2+(18*(getCases()-1)),offsetY+pt.y-49);
//Corner bottom left
path2.lineTo(offsetX+pt.x-20,offsetY+pt.y-49);
//Corner top left
path2.lineTo(offsetX+pt.x-20,offsetY+pt.y-60);
canvas.drawPath(path2, p1);
//Paint stroke above using brush 1
p1.setColor(Color.BLACK);
p1.setAntiAlias(false);
p1.setStyle(Paint.Style.STROKE);
canvas.drawPath(path,p1);
//Draw icons inside map
for(int i = 0;i<icons.length;i++){
canvas.drawBitmap(icons[i],
offsetX+pt.x+(36*(i-1))+18,
offsetY+pt.y-48,
p1);
}
p1.setAntiAlias(true);
p1.setTypeface(Typeface.DEFAULT);
p1.setTextSize(10);
//Draw station occupancy
canvas.drawText(
Integer.toString(msg.getBikes()),
offsetX+pt.x+2,
offsetY+pt.y-36,
p1);
canvas.drawText(
Integer.toString(msg.getParking()),
offsetX+pt.x+2+36,
offsetY+pt.y-36,
p1);
p1.setAntiAlias(true);
p1.setTextSize(9);
p1.setColor(Color.rgb(14,95,160));
p1.setTypeface(Typeface.DEFAULT_BOLD);
//Draw station name above yellow box
canvas.drawText(
msg.getName(),
offsetX+2+pt.x-20,
offsetY+pt.y-51,
p1);
super.draw(canvas, mapView, shadow);
}
/* Utility function to get the number of used "cases"
** this is necessary to know how large the overlay will be
*/
protected int getCases(){
return icons.length+2;
}
/*
* This function will detect if an overlay
* has been tapped or not and return true if so
*/
@Override
public boolean onTap(GeoPoint p, MapView mapView) {
Point pointTap = mapView.getProjection().toPixels(p, null);
Point pointMap = mapView.getProjection().toPixels(point, null);
if(
pointTap.x-pointMap.x>=0
&& pointTap.x-pointMap.x<=80
&& pointMap.y-pointTap.y>=0
&& pointMap.y-pointTap.y<=50
)
{
Log.v("onTap",msg.getName());
((Villo)ctx).showDialogForMessage(msg);
return true;
}
return super.onTap(p, mapView);
}
}
No comments:
Post a Comment