接上篇 http://www.cnblogs.com/inkheart0124/p/3536322.html
1,在地圖上打個標記
1 private MarkerOptions mMarkOption; 2 3 mMarkOption = new MarkerOptions().icon(BitmapDescriptorFactory.fromAsset("target.png")); 4 mMarkOption.draggable(true); 5 6 double dLat = mLocation.getLatitude(); 7 double dLong = mLocation.getLongitude(); 8 9 LatLng latlng = new LatLng(dLat, dLong); 10 11 mMarkOption.position(latlng); 12 mMarkOption.title("title"); 13 mMarkOption.snippet("snippet"); 14 Marker mMarker = mMapView.addMarker(mMarkOption);
3行,MarkerOptions對象,自己設置一個icon( target.png)
4行,設置為可拖動
6~9行,構造當前經緯度的LatLng
11~13行,設置標記的位置,info window的標題title、詳細snippet
14行,GoogleMap的 addMarker(MarkerOptions) 方法,把標記添加到地圖上,返回Marker對象mMarker。
2,拖動標記
設置標記可拖動:
方法一、先設置mMarkOption.draggable(true);,再addMarker;
方法二、Marker的setDraggable(boolean)方法;
Google Map 默認長按標記開始拖動,開發者只需要注冊監聽。注冊拖動事件監聽
mMapView.setOnMarkerDragListener(this);
acitiviy實現OnMarkerDragListener接口如下:
1 /* OnMarkerDragListener start */ 2 @Override 3 public void onMarkerDrag(Marker marker) { 4 } 5 6 @Override 7 public void onMarkerDragEnd(Marker marker) { 8 } 9 10 @Override 11 public void onMarkerDragStart(Marker marker) { 12 if(marker.isInfoWindowShown()) 13 marker.hideInfoWindow(); 14 mMarkerLoaded = false; 15 } 16 /* OnMarkerDragListener end */
3行,public void onMarkerDrag(Marker marker),當Marker拖動的過程中會不斷調用此函數,拖動中marker的位置marker.getPosition()可以得到經緯度。
7行,public void onMarkerDragEnd(Marker marker),拖動結束
11行,public void onMarkerDragStart(Marker marker),開始拖動
11~15行,開始拖動的時候,判斷如果正在顯示info window,則隱藏info window。
3,點擊標記彈出info window
點擊marker的default動作就是顯示info window,之前設置的title和snippet會顯示在infowindow中。
我們對info window做一點小改造,讓他顯示一個小圖標和標記地點的地址
代碼:
activity 實現InfoWindowAdapter接口(implements InfoWindowAdapter)
調用GoogleMap的方法setInfoWindowAdapter()方法
mMapView.setInfoWindowAdapter(this);
activity中實現InfoWindowAdapter的兩個方法:
public View getInfoWindow(Marker marker);返回的View將用於構造整個info window的窗口
public View getInfoContents(Marker marker);返回的View將用於構造info window的顯示內容,保留原來的窗口背景和框架
首先需要定義一個View的布局
res/layout/map_info.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 3 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="wrap_content" 5 android:layout_height="wrap_content" 6 > 7 8 <ImageView 9 android:id="@+id/map_info_image" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:layout_alignParentLeft="true" 13 android:layout_marginLeft="0dip" 14 android:layout_marginTop="0dip" 15 /> 16 17 <LinearLayout 18 android:layout_toRightOf="@id/map_info_image" 19 android:layout_width="200dip" 20 android:layout_height="wrap_content" 21 android:layout_marginLeft="5dip" 22 android:layout_marginTop="0dip" 23 android:orientation="vertical" 24 > 25 <TextView 26 android:id="@+id/map_info_title" 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:text="map_info_title" 30 android:layout_gravity="center" 31 /> 32 33 <TextView 34 android:id="@+id/map_info_snippet" 35 android:layout_width="wrap_content" 36 android:layout_height="wrap_content" 37 android:text="map_info_snippet" 38 android:layout_gravity="center" 39 /> 40 41 </LinearLayout> 42 43 </RelativeLayout>
實現getInfoContents函數:
1 /* GoogleMap.InfoWindowAdapter begin */ 2 private View mInfoWindowContent = null; 3 @Override 4 public View getInfoContents(Marker marker) { 5 6 if(mInfoWindowContent == null){ 7 mInfoWindowContent = mInflater.inflate(R.layout.map_info, null); 8 } 9 10 ImageView infoImage = (ImageView)mInfoWindowContent.findViewById(R.id.map_info_image); 11 infoImage.setImageResource(R.drawable.address); 12 TextView infoTitle = (TextView)mInfoWindowContent.findViewById(R.id.map_info_title); 13 infoTitle.setText(marker.getTitle()); 14 15 TextView infoSnippet = (TextView)mInfoWindowContent.findViewById(R.id.map_info_snippet); 16 infoSnippet.setText(marker.getSnippet()); 17 return mInfoWindowContent; 18 } 19 20 @Override 21 public View getInfoWindow(Marker marker) { 22 return null; 23 }
6~8行,根據布局文件 res/layout/map_info.xml 填充一個View的布局
LayoutInflater mInflater = LayoutInflater.from(this); //this即activity的context
10~11行,設置圖標
12~13行,設置title,marker.getTitle()取出marker中保存的title字符串
15~16行,設置snippet,marker.getSnippet()取出marker的snippet字符串
當點擊標記彈出info window時,首先會跑到getInfoWindow(),如果返回null,就會跑到getInfoContents(),返回的View就顯示到info window中。
4,根據經緯度發查地址,用snippet字段顯示地址信息
首先監聽marker點擊事件
mMapView.setOnMarkerClickListener(this);
activity實現OnMarkerClickListener接口:
/* OnMarkerClickListener start */ @Override public boolean onMarkerClick(Marker marker) { if(mMarkerLoaded == false) getAddressOfMarker(); return false; } /* OnMarkerClickListener end */
即,點擊marker,在彈出info窗口前先去查詢marker所在的經緯度的地理地址。
這個函數要返回false。如果返回true,則表示click事件被消費掉了,就不會再觸發默認動作(即彈出info窗口)。
看下google的geocoder反解地址的過程:
private GetAddressTask mGetAddTack = null; private void getAddressOfMarker(){ if(mGetAddTack != null){ mGetAddTack.cancel(true); } mGetAddTack = new GetAddressTask(this); mGetAddTack.execute(mCarMarker.getPosition()); }
getAddressOfMarker函數中執行了一個異步小任務GetAddressTask,代碼如下:
1 private class GetAddressTask extends AsyncTask<LatLng, Void, String[]>{ 2 Context mContext; 3 4 public GetAddressTask(Context context) { 5 super(); 6 mContext = context; 7 } 8 9 @Override 10 protected void onPreExecute(){ 11 mMarker.setTitle(getResources().getString(R.string.mapAddrLoading)); 12 mMarker.setSnippet(" "); 13 if(mMarker.isInfoWindowShown()) 14 mMarker.showInfoWindow(); 15 } 16 17 @Override 18 protected void onPostExecute(String[] result){ 19 if(result == null) 20 return; 21 22 if(mMarker != null){ 23 if((result[1] != null) && (result[0] != null)){ 24 mMarker.setTitle(result[0]); 25 mMarker.setSnippet(result[1]); 26 if(mMarker.isInfoWindowShown()) 27 mMarker.showInfoWindow(); 28 } 29 else{ 30 mMarker.setTitle(getResources().getString(R.string.mapAddrTitle)); 31 mMarker.setSnippet(getResources().getString(R.string.mapAddrUnknown)); 32 if(mMarker.isInfoWindowShown()) 33 mMarker.showInfoWindow(); 34 }35 } 36 } 37 mMarkerLoaded = true; 38 } 39 40 @Override 41 protected String[] doInBackground(LatLng... params) { 42 LatLng latlng = params[0]; 43 String[] result = new String[2]; 44 45 String urlString = "http://maps.google.com/maps/api/geocode/xml?language=zh-CN&sensor=true&latlng=";//31.1601,121.3962"; 46 HttpGet httpGet = new HttpGet(urlString + latlng.latitude + "," + latlng.longitude); 47 HttpClient httpClient = new DefaultHttpClient(); 48 49 InputStream inputStream = null; 50 HttpResponse mHttpResponse = null; 51 HttpEntity mHttpEntity = null; 52 try{ 53 mHttpResponse = httpClient.execute(httpGet); 54 mHttpEntity = mHttpResponse.getEntity(); 55 inputStream = mHttpEntity.getContent(); 56 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 57 58 String line = ""; 59 String startTag = "<formatted_address>"; 60 String endTag = "</formatted_address>"; 61 while (null != (line = bufferedReader.readLine())){ 62 if(isCancelled()) 63 break; 64 line = line.trim(); 65 String low = line.toLowerCase(Locale.getDefault()); 66 if(low.startsWith(startTag)){ 67 int endIndex = low.indexOf(endTag); 68 String addr = line.substring(startTag.length(), endIndex); 69 if((addr != null) && (addr.length() >0)){ 70 result[1] = addr; 71 result[0] = getResources().getString(R.string.mapAddrTitle); 72 break; 73 } 74 } 75 } 76 } 77 catch (Exception e){ 78 log("Exception in GetAddressTask doInBackground():" + e); 79 } 80 finally{ 81 try{ 82 if(inputStream != null) 83 inputStream.close(); 84 } 85 catch (IOException e){ 86 log("IOException in GetAddressTask doInBackground():" + e); 87 } 88 } 89 return result; 90 } 91 }
重點是41行開始的 doInBackground 函數,向google的geocoder發起一個http請求,請求格式如下:
http://maps.google.com/maps/api/geocode/xml?language=zh-CN&sensor=true&latlng=30.1601,121.3922
返回數據可以是JSON或XML,我這里用的是XML,語言中文zh-CN,latlng=緯度,經度
返回的XML腳本,是指定經緯度附近的有效地址,可能不只一個。我們只取第一個<formatted_address></formatted_address>標簽中的字符串,保存在result中。
18行,在任務執行結束的onPostExcute函數中,調用marker.setSnippet(result[1])保存到snippet中,title則根據任務執行情況設置成合適的String。
26~28行,由於這是一個異步任務,地址取回的時候,info窗口可能已經顯示出來了,這時調用showInfoWindow(),getInfoContents函數會重新跑一次,讓窗口顯示最新取到的title和snippet。