android的api中没有直接提供气球和信息窗口的控件的实现,所以时常要开发带信息窗口的气球实现类,这时需要自定义一个气球overlay来继com.google.android.maps.Overlay
类,overlay就是可以自定义绘制的覆盖在地图上面的一个层。下面是代码:
001 |
package morgen.mstx; |
002 |
003 |
import java.util.ArrayList; |
004 |
import java.util.List; |
005 |
import android.view.MotionEvent; |
006 |
import com.google.android.maps.GeoPoint; |
007 |
import com.google.android.maps.MapView; |
008 |
import com.google.android.maps.Overlay; |
009 |
import com.google.android.maps.Projection; |
010 |
import android.graphics.*; |
011 |
012 |
class MyBallonOverlay extends Overlay{ |
013 |
final static int picWidth= 20 ; //气球图的宽度 |
014 |
final static int picHeight= 34 ; //气球图的高度 |
015 |
final static int arcR= 8 ; //信息窗口的圆角半径 |
016 |
|
017 |
static MyBallonOverlay
currentBallon= null ; //表示当前选中的气球 |
018 |
String
msg; |
019 |
|
020 |
boolean showWindow= false ; |
021 |
|
022 |
GeoPoint
gp; //此气球对应的经纬度 |
023 |
|
024 |
public MyBallonOverlay(GeoPoint
gp,String msg){ |
025 |
this .gp=gp; |
026 |
this .msg=msg; |
027 |
} |
028 |
|
029 |
public GeoPoint
getGeoPoint(){ //获得该气球的经纬度GeoPoint |
030 |
return gp; |
031 |
} |
032 |
|
033 |
@Override |
034 |
public boolean onTouchEvent(MotionEvent
event, MapView mv) { |
035 |
if (currentBallon!= null &¤tBallon!= this ){ |
036 |
//若当前气球不为空且不是自己,什么都不做 |
037 |
return false ; |
038 |
} |
039 |
if (event.getAction()
== MotionEvent.ACTION_DOWN){ |
040 |
//若在气球上按下则设置当前气球为自己,且当前状态为在气球上 |
041 |
int x=( int )event.getX(); |
042 |
int y=( int )event.getY(); |
043 |
Point
p= getPoint(mv); |
044 |
|
045 |
int xb=p.x-picWidth/ 2 ; |
046 |
int yb=p.y-picHeight; |
047 |
|
048 |
if (x>=xb&&x<xb+picWidth&&y>=yb&&y<yb+picHeight){ |
049 |
//若在自己这个气球上按下则设置自己为当前气球 |
050 |
currentBallon= this ; |
051 |
return true ; |
052 |
} |
053 |
} |
054 |
else if (event.getAction()
== MotionEvent.ACTION_MOVE) { |
055 |
//移动事件返回当前气球状态
若当前在气球上则返回true 屏蔽其他移动事件 |
056 |
return currentBallon!= null ; |
057 |
} |
058 |
else if (event.getAction()
== MotionEvent.ACTION_UP) { |
059 |
//获取触控笔位置 |
060 |
int x=( int )event.getX(); |
061 |
int y=( int )event.getY(); |
062 |
|
063 |
//获取气球在屏幕上的坐标范围 |
064 |
Point
p= getPoint(mv); |
065 |
int xb=p.x-picWidth/ 2 ; |
066 |
int yb=p.y-picHeight; |
067 |
|
068 |
if (currentBallon== this &&x>=xb&&x<xb+picWidth&&y>=yb&&y<yb+picHeight){ |
069 |
//若当前气球为自己且在当前气球上抬起触控则显示当前气球内容 |
070 |
//显示完内容后清空当前气球 |
071 |
currentBallon= null ; |
072 |
showWindow=!showWindow; |
073 |
|
074 |
List<Overlay>
overlays = mv.getOverlays(); |
075 |
overlays.remove( this ); //删除此气球再添加 |
076 |
overlays.add( this ); //此气球就位于最上面了 |
077 |
for (Overlay
ol:overlays){ //清除其他气球的showWindow标记 |
078 |
if (ol instanceof MyBallonOverlay){ |
079 |
if (ol!= this ){ |
080 |
((MyBallonOverlay)ol).showWindow= false ; |
081 |
} |
082 |
} |
083 |
} |
084 |
return true ; |
085 |
} |
086 |
else if (currentBallon== this ){ |
087 |
//若当前气球为自己但抬起触控不再自己上则清空气球状态并返回true |
088 |
currentBallon= null ; |
089 |
return true ; |
090 |
} |
091 |
} |
092 |
return false ; |
093 |
} |
094 |
@Override |
095 |
public void draw(Canvas
canvas, MapView mapView, boolean shadow)
{ |
096 |
//将经纬度翻译成屏幕上的XY坐标 |
097 |
Point
p= getPoint(mapView); |
098 |
//在坐标指定位置绘制气球 |
099 |
canvas.drawBitmap(MainActivity.bitmap,
p.x-picWidth/ 2 ,
p.y-picHeight, null ); |
100 |
|
101 |
if (showWindow){ //如果showWindow为true则显示信息窗口 |
102 |
drawWindow(canvas,p, 160 ); |
103 |
} |
104 |
//调用父类绘制 |
105 |
super .draw(canvas,
mapView, shadow); |
106 |
} |
107 |
|
108 |
public Point
getPoint(MapView mapView){ //将经纬度翻译成屏幕上的XY坐标 |
109 |
Projection
projettion = mapView.getProjection(); |
110 |
Point
p = new Point(); |
111 |
projettion.toPixels(gp,
p); |
112 |
return p; |
113 |
} |
114 |
|
115 |
public void drawWindow(Canvas
canvas,Point p, int winWidth){ //绘制信息窗口的方法 |
116 |
int charSize= 15 ; |
117 |
int textSize= 16 ; |
118 |
int leftRightPadding= 2 ; |
119 |
|
120 |
//为信息分行 |
121 |
int lineWidth=winWidth-leftRightPadding* 2 ; //每行的宽度 |
122 |
int lineCharCount=lineWidth/(charSize); //每行字符数 |
123 |
//扫描整个信息字符串分行 |
124 |
ArrayList<String>
alRows= new ArrayList<String>(); |
125 |
String
currentRow= "" ; |
126 |
for ( int i= 0 ;i<msg.length();i++){ |
127 |
char c=msg.charAt(i); |
128 |
if (c!= '\n' ){ |
129 |
currentRow=currentRow+c; |
130 |
} |
131 |
else { |
132 |
if (currentRow.length()> 0 ){ |
133 |
alRows.add(currentRow); |
134 |
} |
135 |
currentRow= "" ; |
136 |
} |
137 |
if (currentRow.length()==lineCharCount){ |
138 |
alRows.add(currentRow); |
139 |
currentRow= "" ; |
140 |
} |
141 |
} |
142 |
if (currentRow.length()> 0 ){ |
143 |
alRows.add(currentRow); |
144 |
} |
145 |
int lineCount=alRows.size(); //总行数 |
146 |
int winHeight=lineCount*(charSize)+ 2 *arcR; //窗体高度 |
147 |
//创建paint对象 |
148 |
Paint
paint= new Paint(); |
149 |
paint.setAntiAlias( true ); |
150 |
paint.setTextSize(textSize); |
151 |
//绘制
信息窗体圆角矩形 |
152 |
paint.setARGB( 255 , 255 , 251 , 239 ); |
153 |
int x1=p.x-winWidth/ 2 ; |
154 |
int y1=p.y-picHeight-winHeight- 1 ; |
155 |
int x2=x1+winWidth; |
156 |
int y2=y1+winHeight; |
157 |
RectF
r= new RectF(x1,y1,x2,y2); |
158 |
canvas.drawRoundRect(r,
arcR, arcR, paint); |
159 |
paint.setARGB( 255 , 198 , 195 , 198 ); |
160 |
paint.setStyle(Paint.Style.STROKE); |
161 |
paint.setStrokeWidth( 2 ); |
162 |
canvas.drawRoundRect(r,
arcR, arcR, paint); |
163 |
|
164 |
//循环绘制每行文字 |
165 |
paint.setStrokeWidth( 0 ); |
166 |
paint.setARGB( 255 , 10 , 10 , 10 ); |
167 |
int lineY=y1+arcR+charSize; |
168 |
for (String
lineStr:alRows){ |
169 |
System.out.println( "lineStr" +lineStr); |
170 |
for ( int j= 0 ;j<lineStr.length();j++){ |
171 |
String
colChar=lineStr.charAt(j)+ "" ; |
172 |
canvas.drawText(colChar,
x1+leftRightPadding+j*charSize, lineY, paint); |
173 |
} |
174 |
lineY=lineY+charSize; |
175 |
} |
176 |
} |
177 |
} |
上述的draw方法完成了在指定的未知绘制气球图片,并根据气球信息窗口的标志值来决定是否调用drawWindow方法来绘制气球对应的信息窗口。这个自定义的气球可以服务于
大部分涉及到谷歌地图的应用,我有很多应用的涉及到了这些代码,很是实用。