Android 解決Map根據地址返回經緯度方法getFromLocationName()不能正常執行

yangxi_001發表於2014-08-21
android.location 包提供了一些工具來實現基於位置的服務。主要包括 Geocoder 類和LocationManager服務。首先介紹 Geocoder。 

1.使用Android進行地理編碼
 
     如果打算使用地圖做一些實際的事情,可能必須將地址(或位置)轉換為緯度/經度對。此概念稱為地理編碼,android.location.Geocoder 類提供了此功能。實際上,Geocoder既提供了前向轉換,也提供了後向轉換--------它可以獲取地址並返回經度/緯度,也可以將經度/緯度對轉換為一組地址。該類提供了以下方法。 
Java程式碼  收藏程式碼
  1. List<Address>  getFromLocation(double latitude,double longitude,int maxResults);  
  2. List<Address> getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude, double lowerLeftLongitue, double upperRightLatitude, double upperRightLongitude);  
  3. List<Address>  getFromLocationName(String locationName, int maxResults)。  

      事實證明,計算地址並不完全屬於科學範疇,因為可以通過各種方式來描述位置。例如,getFromLocationName() 方法可以獲得地方的名稱、實體地址和機場編號,或者該位置的流行名稱。因此,這些方法提供了一個地址列表,而不是一個地址。因為這些方法返回一個列表,所以最好提供1~5的maxResults 值來限制結果集,下面我們來看一個查詢地址的例子,和原來一樣我們得自己定義一個類來繼承MapActivity,先來看看執行效果。 

 

  我們的佈局檔案 
Xml程式碼  收藏程式碼
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.     <LinearLayout android:layout_width="fill_parent"  
  8.         android:layout_alignParentBottom="true"  
  9.         android:layout_height="wrap_content" android:orientation="vertical">  
  10.         <EditText  
  11.             android:id="@+id/location"  
  12.             android:layout_width="fill_parent"  
  13.             android:layout_height="wrap_content"  
  14.             android:text="請輸入地址..."/>  
  15.         <Button  
  16.             android:id="@+id/geocodeBtn"  
  17.             android:layout_width="wrap_content"  
  18.             android:layout_height="wrap_content"  
  19.             android:text="Find Location" />  
  20.     </LinearLayout>  
  21.   
  22.           
  23.         <com.google.android.maps.MapView  
  24.             android:id="@+id/geoMap"  
  25.             android:clickable="true"  
  26.             android:layout_width="fill_parent"  
  27.             android:layout_height="320px"  
  28.             android:apiKey="0XemFEdFemEDqY3dE3Ko9ELJX12MRLjJGKEJ01g"  
  29.                  />  
  30.               
  31. </RelativeLayout>  


  AndroidManifest.xml檔案 
Xml程式碼  收藏程式碼
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="xiaohang.zhimeng" android:versionCode="1" android:versionName="1.0">  
  4.     <uses-sdk android:minSdkVersion="10" />  
  5.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  6.         <uses-library android:name="com.google.android.maps" />  
  7.         <activity android:name=".MainActivity" android:label="@string/app_name">  
  8.             <intent-filter>  
  9.                 <action android:name="android.intent.action.MAIN" />  
  10.                 <category android:name="android.intent.category.LAUNCHER" />  
  11.             </intent-filter>  
  12.         </activity>  
  13.     </application>  
  14.     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  
  15.     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
  16.     <uses-permission android:name="android.permission.INTERNET" />  
  17. </manifest>  


   我們的MainActivity類 
Java程式碼  收藏程式碼
  1. package xiaohang.zhimeng;  
  2.   
  3. import java.util.List;  
  4.   
  5. import com.google.android.maps.GeoPoint;  
  6. import com.google.android.maps.MapActivity;  
  7. import com.google.android.maps.MapView;  
  8. import android.location.Address;  
  9. import android.location.Geocoder;  
  10. import android.os.Bundle;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.Button;  
  14. import android.widget.EditText;  
  15.   
  16. public class MainActivity extends MapActivity {  
  17.   
  18.     Geocoder geocoder = null;  
  19.     MapView mapView = null;  
  20.   
  21.     @Override  
  22.     protected boolean isLocationDisplayed() {  
  23.         return false;  
  24.     }  
  25.   
  26.     @Override  
  27.     protected boolean isRouteDisplayed() {  
  28.         return false;  
  29.     }  
  30.   
  31.     @Override  
  32.     public void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.main);  
  35.         mapView = (MapView) findViewById(R.id.geoMap);  
  36.         mapView.setBuiltInZoomControls(true);  
  37.         // 經度:116.3946533203125  
  38.         // 緯度:39.87601941962116  
  39.         int lat = (int) (39.87601941962116 * 1E6);  
  40.         int lng = (int) (116.3946533203125 * 1E6);  
  41.         GeoPoint pt = new GeoPoint(lat, lng);  
  42.         mapView.getController().setZoom(10);  
  43.         mapView.getController().setCenter(pt);  
  44.   
  45.         Button geoBtn = (Button) findViewById(R.id.geocodeBtn);  
  46.   
  47.         geocoder = new Geocoder(this);  
  48.   
  49.         geoBtn.setOnClickListener(new OnClickListener() {  
  50.             @Override  
  51.             public void onClick(View v) {  
  52.                 try {  
  53.                     EditText loc = (EditText) findViewById(R.id.location);  
  54.                     String locationName = loc.getText().toString();  
  55.   
  56.                     List<Address> addressList = geocoder.getFromLocationName(  
  57.                             locationName, 5);  
  58.                     if (addressList != null && addressList.size() > 0) {  
  59.                         int lat = (int) (addressList.get(0).getLatitude() * 1E6);  
  60.                         int lng = (int) (addressList.get(0).getLongitude() * 1E6);  
  61.   
  62.                         GeoPoint pt = new GeoPoint(lat, lng);  
  63.                         mapView.getController().setZoom(15);  
  64.                         mapView.getController().setCenter(pt);  
  65.                     }  
  66.                 } catch (Exception e) {  
  67.                     e.printStackTrace();  
  68.                 }  
  69.             }  
  70.         });  
  71.     }  
  72. }  


   但是如果這個類要是這麼寫,就會有問題了。但是感覺這麼寫是沒錯誤的,但是每當你點選一次Find Location按鈕就會出現一次異常(IOException),異常見下圖。 



    說什麼服務不可以用,去網上一搜 遇見這問題的人還真不少,eoe上邊也有 但是沒說怎麼解決,不了了之了。至於為什麼有這個異常,我也不能準確的告訴大家 我也不太清楚,網上說什麼的都有。 什麼 是bug之類的 等等,大家可以去網上搜搜 最後自己這樣寫一次試試。但是解決方法還是有的,在這裡http://code.google.com/p/android/issues/detail?id=8816  老外搞出來的方法,他們討論的也很火熱,感覺比我們 積極很多。大家看 21樓就行了, 呵呵。 

   具體解決方法就是自己定義了兩個靜態方法,我把這兩個方法放到了MapUtility類中,這個類是我自己定義的。 
MapUtility類 
Java程式碼  收藏程式碼
  1. package xiaohang.zhimeng.tool;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import org.apache.http.HttpEntity;  
  6. import org.apache.http.HttpResponse;  
  7. import org.apache.http.client.ClientProtocolException;  
  8. import org.apache.http.client.HttpClient;  
  9. import org.apache.http.client.methods.HttpGet;  
  10. import org.apache.http.impl.client.DefaultHttpClient;  
  11. import org.json.JSONArray;  
  12. import org.json.JSONException;  
  13. import org.json.JSONObject;  
  14.   
  15. import com.google.android.maps.GeoPoint;  
  16.   
  17. public class MapUtility {  
  18.     public static JSONObject getLocationInfo(String address) {  
  19.   
  20.         HttpGet httpGet = new HttpGet("http://maps.google."  
  21.                 + "com/maps/api/geocode/json?address=" + address  
  22.                 + "ka&sensor=false");  
  23.         HttpClient client = new DefaultHttpClient();  
  24.         HttpResponse response;  
  25.         StringBuilder stringBuilder = new StringBuilder();  
  26.   
  27.         try {  
  28.             response = client.execute(httpGet);  
  29.             HttpEntity entity = response.getEntity();  
  30.             InputStream stream = entity.getContent();  
  31.             int b;  
  32.             while ((b = stream.read()) != -1) {  
  33.                 stringBuilder.append((char) b);  
  34.             }  
  35.         } catch (ClientProtocolException e) {  
  36.         } catch (IOException e) {  
  37.         }  
  38.   
  39.         JSONObject jsonObject = new JSONObject();  
  40.         try {  
  41.             jsonObject = new JSONObject(stringBuilder.toString());  
  42.         } catch (JSONException e) {  
  43.             e.printStackTrace();  
  44.         }  
  45.   
  46.         return jsonObject;  
  47.     }  
  48.   
  49.     // After executing this, another method converts that JSONObject into a  
  50.     // GeoPoint.  
  51.   
  52.     public static GeoPoint getGeoPoint(JSONObject jsonObject) {  
  53.   
  54.         Double lon = new Double(0);  
  55.         Double lat = new Double(0);  
  56.   
  57.         try {  
  58.   
  59.             lon = ((JSONArray) jsonObject.get("results")).getJSONObject(0)  
  60.                     .getJSONObject("geometry").getJSONObject("location")  
  61.                     .getDouble("lng");  
  62.   
  63.             lat = ((JSONArray) jsonObject.get("results")).getJSONObject(0)  
  64.                     .getJSONObject("geometry").getJSONObject("location")  
  65.                     .getDouble("lat");  
  66.   
  67.         } catch (JSONException e) {  
  68.             e.printStackTrace();  
  69.         }  
  70.         return new GeoPoint((int) (lat * 1E6), (int) (lon * 1E6));  
  71.     }  
  72.   
  73. }  


   這兩個方法就不多解釋了,反正我們最終的目的是需要一個GeoPoint物件,它給我們返回正確的GeoPoint物件就行了,大家可以去原地址去看看他們怎麼討論了,連結在上邊我已經貼出來了,現在我們在搜尋tian an men 就會把tian an men給我們顯示在地圖的中央了,沒修改之前是 一點 一次異常,現在的效果如下圖,(大家知道 tian an men是啥意思吧,自動eye被 oracle搞過一次 法律意識越來越強啊。。。。  ) 

 

     要體驗地理編碼在Android中的使用,可以在 EditText欄位中鍵入位置名稱或它的地址,然後點選 Find Location按鈕。要找到某個位置的地址,呼叫 Geocoder的 getFromLocationName()方法。位置可以是地址或流行名稱,比如 “故宮”。地理編碼是一項實時操作,所以根據Android文件的建議,我們建議將結果限制為5個。對getFromLocationName()的呼叫返回一個地址列表。示例程式獲取該地址列表並處理第一個地址(如果存在)。每個地址都具有經緯度。可以使用它來建立 GeoPoint。然後呼叫地圖控制器並導航到該點。縮放級別可以設定為1~21 (包括1和21)的整數。在從1向21移動時,縮放級別每次將增加兩級。 

      對於地理編碼,應該瞭解幾點。第一,返回的地址並不總是準確的地址。顯然,由於返回的地址列表取決於輸入的準確度,所以需要儘量向 Geocoder 提供準確的位置名稱。第二,儘量將 maxResults 設定為1~5的值。最後,應該認真考慮在不同於UI執行緒的執行緒中執行地理編碼操作。這有兩個原因。第一個很明顯:這項操作很耗時,而且你一定不希望UI在進行地理編碼時停頓,如果停頓會阻塞整個使用者介面。當在執行一些耗時的操作的時候,不能及時地分發 事件,包括使用者介面重繪事件。從使用者的角度來看,應用程式看上去像掛掉了。更糟糕的是,如果阻塞應用程式的時間過長(5秒鐘)Android會向使用者提示 一些資訊,即開啟一個“應用程式沒有相應(application not responding)”ANR 的對話方塊,關於執行緒的更多內容請大家看這裡http://byandby.iteye.com/blog/825071  第二個原因是,對於移動裝置,始終需要假設網路連線可能丟失並且連線很弱。因此,需要恰當地處理I/O 異常和超時。計算出地址以後,就可以將結果傳送給UI執行緒,下面我們看看使用後臺執行緒進行地理編碼,這個例子和上一個例子一樣 唯一不同的就是 這個例子把查詢地址的操作放到 另外一個輔助執行緒裡來執行了,上一個是所有的操作都在UI執行緒裡,下面是我們的Activity類,GeocodingDemoActivity,我們看看怎麼實現。。。 

  GeocodingDemoActivity類 
Java程式碼  收藏程式碼
  1. package xiaohang.zhimeng;  
  2.   
  3. import java.util.List;  
  4. import org.json.JSONObject;  
  5. import xiaohang.zhimeng.tools.MapUtility;  
  6. import android.app.AlertDialog;  
  7. import android.app.Dialog;  
  8. import android.app.ProgressDialog;  
  9. import android.location.Address;  
  10. import android.location.Geocoder;  
  11. import android.os.Bundle;  
  12. import android.os.Handler;  
  13. import android.os.Message;  
  14. import android.view.View;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.Button;  
  17. import android.widget.EditText;  
  18. import com.google.android.maps.GeoPoint;  
  19. import com.google.android.maps.MapActivity;  
  20. import com.google.android.maps.MapView;  
  21.   
  22. public class GeocodingDemoActivity extends MapActivity {  
  23.   
  24.     Geocoder geocoder = null;  
  25.     MapView mapView = null;  
  26.     ProgressDialog progDialog = null;  
  27.     List<Address> addressList = null;  
  28.   
  29.     @Override  
  30.     protected boolean isLocationDisplayed() {  
  31.         return false;  
  32.     }  
  33.   
  34.     @Override  
  35.     protected boolean isRouteDisplayed() {  
  36.         return false;  
  37.     }  
  38.   
  39.     @Override  
  40.     protected void onCreate(Bundle icicle) {  
  41.         super.onCreate(icicle);  
  42.         setContentView(R.layout.main);  
  43.         mapView = (MapView) findViewById(R.id.geoMap);  
  44.         mapView.setBuiltInZoomControls(true);  
  45.         // 北京經緯度  
  46.         // 經度:116.3946533203125  
  47.         // 緯度:39.87601941962116  
  48.         int lat = (int) (39.87601941962116 * 1000000);  
  49.         int lng = (int) (116.3946533203125 * 1000000);  
  50.         GeoPoint pt = new GeoPoint(lat, lng);  
  51.         mapView.getController().setZoom(10);  
  52.         mapView.getController().setCenter(pt);  
  53.   
  54.         Button geoBtn = (Button) findViewById(R.id.geocodeBtn);  
  55.         geoBtn.setOnClickListener(new OnClickListener() {  
  56.             @Override  
  57.             public void onClick(View v) {  
  58.                 EditText loc = (EditText) findViewById(R.id.location);  
  59.                 String locationName = loc.getText().toString();  
  60.                 progDialog = ProgressDialog.show(GeocodingDemoActivity.this,  
  61.                         "Processing.....""Finding Location"truefalse);  
  62.                 findLocation(locationName);  
  63.   
  64.             }  
  65.         });  
  66.     }  
  67.   
  68.     private void findLocation(final String locationName) {  
  69.   
  70.         Thread thrd = new Thread() {  
  71.             @Override  
  72.             public void run() {  
  73.                 System.out.println("執行緒Name是:"  
  74.                         + Thread.currentThread().getName());  
  75.                 try {  
  76.                     // do backgrond work  
  77.                     JSONObject jo = MapUtility.getLocationInfo(locationName);  
  78.                     GeoPoint gp = MapUtility.getGeoPoint(jo);  
  79.                     Message message = uiCallback.obtainMessage();  
  80.                     message.obj = gp;  
  81.                     message.sendToTarget();  
  82.                 } catch (Exception e) {  
  83.                     e.printStackTrace();  
  84.                 }  
  85.             }  
  86.         };  
  87.         thrd.start();  
  88.     }  
  89.   
  90.     // ui thread callback handler  
  91.     private Handler uiCallback = new Handler() {  
  92.         public void handleMessage(android.os.Message msg) {  
  93.             System.out.println("執行緒Name是:" + Thread.currentThread().getName());  
  94.             progDialog.dismiss();  
  95.             GeoPoint pt = (GeoPoint) msg.obj;  
  96.             if (pt != null) {  
  97.                 mapView.getController().setZoom(15);  
  98.                 mapView.getController().setCenter(pt);  
  99.             } else {  
  100.                 Dialog foundNothingDlg = new AlertDialog.Builder(  
  101.                         GeocodingDemoActivity.this).setIcon(0)  
  102.                         .setTitle("Failed to Find Location")  
  103.                         .setPositiveButton("OK"null)  
  104.                         .setMessage("Location Not Found").create();  
  105.                 foundNothingDlg.show();  
  106.             }  
  107.         };  
  108.     };  
  109. }  


   這次我們再來看看執行效果。 

 

 

 

   從下圖我們能看出 查詢操作在不同的執行緒中完成。 

 

  最後在提醒大家如果下載原始碼 注意替換成自己的 金鑰。 
  
  原始碼已上傳 
  • test_location.rar (52.4 KB)
  • 描述: 查詢操作在輔助執行緒完成
  • 下載次數: 104

相關文章