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方法来绘制气球对应的信息窗口。这个自定义的气球可以服务于
大部分涉及到谷歌地图的应用,我有很多应用的涉及到了这些代码,很是实用。