940 likes | 1.06k Views
第 14 章 Google 地圖與定位服務. 14-1 定位服務 - 我在哪裡 14-2 地圖解碼服務 - 找出景點座標 14-3 本地服務與定位應用 -GPS 景點防撞雷達 14-4 使用 Google Maps API-My 地圖 14-5 標記 Google 地圖 - 追蹤個人行蹤. 14-1 定位服務-我在哪裡. 14-1-1 Android 的定位服務與座標 14-1-2 使用定位服務 - 我在哪裡. 14-1-1 Android 的定位服務與座標 - 說明.
E N D
第14章 Google地圖與定位服務 • 14-1 定位服務-我在哪裡 • 14-2 地圖解碼服務-找出景點座標 • 14-3 本地服務與定位應用-GPS景點防撞雷達 • 14-4 使用Google Maps API-My地圖 • 14-5 標記Google地圖-追蹤個人行蹤
14-1 定位服務-我在哪裡 • 14-1-1 Android的定位服務與座標 • 14-1-2 使用定位服務-我在哪裡
14-1-1 Android的定位服務與座標-說明 • Android行動裝置結合定位功能和Google地圖建立的「位置感知服務」(Location-based Service,LBS),這是一項十分實用的功能,LBS應用程式可以追蹤你的位置和提供一些額外服務,例如:找出附近的咖啡廳、停車場、自動櫃員機或加油站等。 • Android作業系統提供LocationManager類別的定位服務來幫助我們存取行動裝置目前的定位資料,包含:緯度(Latitude)、經度(Longitude)和高度(Altitude)等。
14-1-1 Android的定位服務與座標-種類 • GPS定位提供者:提供者名稱字串為"gps",它是使用GPS(Global Positioning System)的衛星訊號來定位,可以提供精確的位置資訊,但是無法收到衛星訊號的室內並無法使用。 • 網路定位提供者;提供者名稱字串為"network",它是直接使用電信公司基地台來執行三角定位,其提供的位置資訊較不精確,但是可以在室內使用。
14-1-1 Android的定位服務與座標-座標 • 定位服務最主要的目的是找出行動裝置目前位置的經緯度座標,經緯度是經度與緯度合稱的座標系統,也稱為地理座標系統,它是使用三度空間的球面來定義地球表面各點的座標系統,能夠標示地球表面上的任何一個位置。經度與緯度的說明,如下所示: • 緯度:地球表面某一點距離地球赤道以南或以北的度數,其值為0至90度,赤道以北的緯度叫北緯(符號為N);赤道以南的緯度稱南緯(符號為S)。 • 經度:地球表面上某一點距離本初子午線(一條南北方向經過倫敦格林威治天文台舊址的子午線)以東或以西的度數,簡單的說,本初子午線的經度是0度,其他地點的經度是向東從0到180度,即東經(符號為W)或向西從0到180度,即西經(符號為E)。
14-1-2 使用定位服務-我在哪裡(說明) • 我在哪裡是定位服務的最簡單應用,可以顯示目前行動裝置的經緯度座標。
14-1-2 使用定位服務-我在哪裡步驟一:開啟和執行Android專案 • 請啟動Eclipse IDE開啟Android專案Ch14_1_2,內含1個Java類別檔和版面配置檔main.xml,執行可以看到程式顯示目前的位置座標,按【顯示Google地圖】鈕,可以啟動Google地圖顯示此座標附近的地圖,即台北火車站,如下圖所示:
14-1-2 使用定位服務-我在哪裡步驟二:建立我在哪裡使用介面的版面配置 • 我在哪裡使用介面的版面配置是定義在main.xml檔,使用LinearLayout垂直編排1個TextView和Button元件,如下所示: <TextView android:id="@+id/output" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="顯示Google地圖" android:onClick="button1_Click"/>
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-1 • 在Ch14_1_2Activity活動類別的開頭宣告成員的LocationManager和Location物件變數,如下所示: public class Ch14_1_2Activity extends Activity { private LocationManager manager; private Location currentLocation; private String best; … }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-2 onCreate()方法 • 在覆寫的onCreate()方法載入版面配置後,可以取得系統服務的LocationManager物件,if條件檢查是否有啟用GPS,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); manager = (LocationManager)getSystemService( LOCATION_SERVICE); if (!manager.isProviderEnabled( LocationManager.GPS_PROVIDER)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("定位管理") .setMessage("GPS目前狀態是尚未啟用.\n"
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-3 +"請問你是否現在就設定啟用GPS?") .setPositiveButton("啟用", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent i = new Intent(Settings. ACTION_LOCATION_SOURCE_SETTINGS); startActivity(i); } }) .setNegativeButton("不啟用", null).create().show(); } }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-4 onResume()方法 • 在覆寫onResume()方法建立Criteria物件設定如何選擇提供者,以便取得最佳或符合你需求的定位提供者,然後就可以呼叫getrBestProvider()方法取得最佳提供者字串,參數是Criteria物件,如下所示: @Override protected void onResume() { super.onResume(); Criteria criteria = new Criteria(); best = manager.getBestProvider(criteria, true); int minTime = 5000; // 毫秒 float minDistance = 5; // 公尺 if (best != null) { currentLocation = manager.getLastKnownLocation(best);
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-5 manager.requestLocationUpdates(best, minTime, minDistance, listener); } else { currentLocation = manager.getLastKnownLocation( LocationManager.GPS_PROVIDER); manager.requestLocationUpdates( LocationManager.GPS_PROVIDER, minTime, minDistance, listener); } updatePosition(); }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-6 onPause()方法 • 在覆寫onPause()方法呼叫removeUpdates()方法取消周期更新位置,如下所示: @Override protected void onPause() { super.onPause(); manager.removeUpdates(listener); }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-7 updatePosition()方法 • 在自訂updatePosition()方法更新TextView元件顯示的位置資訊,如下所示: private void updatePosition() { TextView output; output = (TextView) findViewById(R.id.output); if (currentLocation == null) { output.setText("取得定位資訊中..."); } else { output.setText(getLocationInfo(currentLocation)); } }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-8 LocationListener傾聽者物件 • 使用匿名內層類別實作LocationListener介面來建立此物件,需要實作4個方法,如下所示: private LocationListener listener = new LocationListener() { @Override public void onLocationChanged(Location location) { currentLocation = location; updatePosition(); } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } };
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-9 getLocationInfo()方法 • 在自訂的getLocationInfo()方法可以從參數Loaction物件取得定位資訊,如下所示: public String getLocationInfo(Location location) { StringBuffer str = new StringBuffer(); str.append("定位提供者(Provider): "+ location.getProvider()); str.append("\n緯度(Latitude): " + Double.toString(location.getLatitude())); str.append("\n經度(Longitude): " + Double.toString(location.getLongitude())); str.append("\n高度(Altitude): " + Double.toString(location.getAltitude())); return str.toString(); }
14-1-2 使用定位服務-我在哪裡步驟三:建立Activity活動類別使用定位服務-10 button1_Click()事件處理方法 • button1_Click()事件處理方法可以依據目前的位置座標來啟動Google地圖,它是使用Intent物件啟動Google地圖程式,首先取得經緯度座標longitude和latitude,如下所示: public void button1_Click(View view) { float latitude = (float) currentLocation.getLatitude(); float longitude = (float) currentLocation.getLongitude(); String uri = String.format("geo:%f,%f?z=18", latitude, longitude); Intent geoMap = new Intent( Intent.ACTION_VIEW,Uri.parse(uri)); startActivity(geoMap); }
14-1-2 使用定位服務-我在哪裡步驟四:在AndroidManifest.xml新增權限 • 因為需要使用定位服務,所以在AndroidManifest.xml檔新增2個權限,ACCESS_COURSE_LOCATION是網路定位服務,ACCESS_FINE_LOCATION是GPS定位服務,如下所示: <uses-permission android:name= "android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION"/>
14-2 地圖解碼服務-找出景點座標(說明) • 地圖解碼服務(Geocoding Services)可以從位置名稱、郵遞區號等資訊來找出經緯度座標,或反過來,從經緯度座標找出位置名稱或地址。 • Android是使用Geocoder類別來處理座標轉換,相關方法的說明,如下表所示:
14-2 地圖解碼服務-找出景點座標步驟一:開啟和執行Android專案 • 請啟動Eclipse IDE開啟Android專案Ch14_2,內含1個Java類別檔和版面配置檔main.xml,因為Android模擬器不支援地圖解碼服務,筆者是使用2.3版的實機來測試,其執行結果如下圖所示:
14-2 地圖解碼服務-找出景點座標步驟二:建立找出景點座標使用介面的版面配置-1 • 找出景點座標使用介面的版面配置是定義在main.xml檔,使用LinearLayout垂直編排數個水平的LinearLayout,內含TextView和EditText元件輸入座標和景點名稱,如下所示: <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="緯度(Latitude):"/>
14-2 地圖解碼服務-找出景點座標步驟二:建立找出景點座標使用介面的版面配置-2 <EditText android:id="@+id/txtLat" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="25.047924" android:inputType="numberDecimal"/> </LinearLayout> • EditText元件輸入緯度,另有2個是輸入經度和景點名稱,查詢結果的地址是顯示在Spinner元件,如下所示: <Spinner android:id="@+id/addresslist" android:layout_width="fill_parent" android:layout_height="wrap_content"/> • 至於查詢的經緯度是顯示在TextView元件,Button元件button1~3的事件處理方法為button1~3_Click()。
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-1 • 在Ch14_2Activity活動類別的開頭宣告成員的ArrayAdapter和Geocoder等物件變數,如下所示: public class Ch14_2Activity extends Activity { private final int maxResult = 3; private String addressList[] = new String[maxResult]; private ArrayAdapter<String> adapter; private TextView output; private Geocoder geocoder; private EditText lat, lon; … }
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-2 onCreate()方法 • 在覆寫的onCreate()方法載入版面配置後,可以取得EditText物件來取得輸入座標,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lat = (EditText) findViewById(R.id.txtLat); lon = (EditText) findViewById(R.id.txtLong); output = (TextView)findViewById(R.id.output); geocoder = new Geocoder(this, Locale.TAIWAN); }
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-3 button1_Click()事件處理方法 • 在button1_Click()事件處理方法將經緯度座標轉換成地址,首先將經緯度的輸入字串轉換成float浮點數,如下所示: public void button1_Click(View view) { float latitude = Float.parseFloat(lat.getText().toString()); float longitude = Float.parseFloat(lon.getText().toString()); try { List<Address> listAddress = geocoder.getFromLocation( latitude, longitude, maxResult); • 程式碼呼叫Geocoder物件的getFromLocation()方法取得地址清單的List物件,參數依序是緯度、經度和最多傳回的地址數。
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-4 • 如果有傳回地址,就在下方if條件的程式區塊建立ArrayAdapter結合器物件,然後顯示在Spinner元件,如下所示: if (listAddress != null) { Spinner spinner = (Spinner)findViewById(R.id.addresslist); for (int j = 0; j < maxResult; j++) addressList[j] = "N/A"; int index = 0; for (int j = 0; j < maxResult; j++) { Address findAddress = listAddress.get(j); StringBuilder strAddress = new StringBuilder(); for (int i = 0; i < findAddress.getMaxAddressLineIndex(); i++) { String str = findAddress.getAddressLine(i); strAddress.append(str).append("\n"); } if (strAddress.length() > 0) { addressList[index++] = strAddress.toString(); } }
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-5 • 在建立addressList[]陣列後,可以將此陣列建立成ArrayAdapter結合器物件,如下所示: adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, addressList); adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item); spinner.setAdapter(adapter); } else { output.setText("注意: 沒有傳回地址資料!"); } } catch (Exception ex) { output.setText("錯誤:" + ex.toString()); } }
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-6 button2_Click()事件處理方法 • 在button2_Click()事件處理方法將地址轉換成經緯度座標,首先取得使用者在EditText元件輸入的名稱,如下所示: public void button2_Click(View view) { EditText address = (EditText) findViewById(R.id.txtAddress); String addressName = address.getText().toString(); try { List<Address> listGPSAddress = geocoder. getFromLocationName(addressName, 1); • 上述程式碼呼叫Geocoder物件的getFromLocationName()方法搜尋經緯度座標,第1個參數是名稱,第2個參數最多傳回幾個座標。
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-7 • 如果有傳回座標,就取出緯度和經度且將它顯示出來,如下所示: if (listGPSAddress != null) { double latitude = listGPSAddress.get(0).getLatitude(); double longitude = listGPSAddress.get(0).getLongitude(); output.setText("緯度: " + latitude + "\n經度: " + longitude); lat.setText(String.valueOf(latitude)); lon.setText(String.valueOf(longitude)); } } catch (Exception ex) { output.setText("錯誤:" + ex.toString()); } }
14-2 地圖解碼服務-找出景點座標步驟三:建立Activity活動類別找出景點座標-8 button3_Click()事件處理方法 • button3_Click()事件處理方法使用Intent物件啟動Google地圖,它和第14-1-2節的button1_Click()事件處理方法相同,筆者就不重複說明。
14-2 地圖解碼服務-找出景點座標步驟四:在AndroidManifest.xml新增存取Internet權限 • 找出景點座標因為需要連線Internet,所以在AndroidManifest.xml檔需要新增INTERNET權限,如下所示: <uses-permission android:name="android.permission.INTERNET"/>
14-3 本地服務與定位應用-GPS景點防撞雷達 • GPS景點防撞雷達是本地服務與定位服務的應用,可以在行動裝置接近輸入的景點座標時,發出警告,其他可能的應用包含危險區域警示,和標示目標區域來檢查是否已經到達其範圍之中。
14-3 本地服務與定位應用-GPS景點防撞雷達步驟一:開啟和執行Android專案 • 請啟動Eclipse IDE開啟Android專案Ch14_3,內含3個Java類別檔和版面配置檔main.xml,在執行前,請先在Android模擬器新增GPS硬體支援,然後就可以執行此專案,其執行結果如下圖所示:
14-3 本地服務與定位應用-GPS景點防撞雷達步驟二:建立GPS景點防撞雷達使用介面的版面配置 • GPS景點防撞雷達使用介面的版面配置是定義在main.xml檔,使用LinearLayout垂直編排3個水平LinearLayout,內含TextView和EditText元件輸入座標和Button按鈕元件,如下所示: <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="經度(Longitude):"/> <EditText android:id="@+id/txtLong" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="121.51617" android:inputType="numberDecimal"/> </LinearLayout>
14-3 本地服務與定位應用-GPS景點防撞雷達步驟三:建立Activity活動類別的事件處理方法-1 • 在Ch14_3Activity活動類別的開頭宣告成員的EditText和TextView物件變數,如下所示: public class Ch14_3Activity extends Activity { private EditText lat, lon; private TextView output; … }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟三:建立Activity活動類別的事件處理方法-2 onCreate()方法 • 在覆寫的onCreate()方法載入版面配置後,可以取得EditText和TextView物件,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lat = (EditText) findViewById(R.id.txtLat); lon = (EditText) findViewById(R.id.txtLong); output = (TextView) findViewById(R.id.output); }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟三:建立Activity活動類別的事件處理方法-3 start_Click()事件處理方法 • 在start_Click()事件處理方法建立Intent物件後,呼叫startService()方法啟動GPSService.class服務,並且傳遞經緯度座標LATITUDE和LONGITUDE,如下所示: public void start_Click(View view) { float latitude = Float.parseFloat(lat.getText().toString()); float longitude = Float.parseFloat(lon.getText().toString()); Intent intent = new Intent(this, GPSService.class); intent.putExtra("LATITUDE", latitude); intent.putExtra("LONGITUDE", longitude); startService(intent); • output.setText("服務啟動中..."); }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟三:建立Activity活動類別的事件處理方法-4 stop_Click()事件處理方法 • 在stop_Click()事件處理方法建立Intent物件後,呼叫stopService()方法停止GPSService.class服務,如下所示: public void stop_Click(View view) { Intent intent = new Intent(this, GPSService.class); stopService(intent); output.setText("服務停止中..."); }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟三:建立Activity活動類別的事件處理方法-5 finish_Click()事件處理方法 • 在finish_Click()事件處理方法呼叫finish()方法結束活動,如下所示: public void finish_Click(View view) { finish(); }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-1 • 在GPSService服務類別使用GPS定位服務來檢查是否距離景點100公尺以內,如果是,送出廣播給GPSReceiver類別。 • GPSService服務類別繼承Service類別且實作LocationListener介面,在開頭宣告LocationManager物件變數,如下所示: public class GPSService extends Service implements LocationListener { private LocationManager manager; private boolean isInArea; private double latitude, longitude; …. }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-2 onCreate()方法 • 在onCreate()方法取得LOCATION_SERVICE系統服務的LocationManager物件後,註冊類別本身為傾聽者物件來定時更新位置資訊,如下所示: @Override public void onCreate() { manager = (LocationManager) getSystemService(LOCATION_SERVICE); manager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 10000, 1, this); isInArea = false; }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-3 onStartCommand()方法 • 在onStartCommand()方法取得Intent物件傳遞的經緯度座標LATITUDE和LONGITUDE,如下所示: @Override public int onStartCommand(Intent intent, int flags, int startId) { latitude = (double) intent.getFloatExtra( "LATITUDE", 40.422005f); longitude = (double) intent.getFloatExtra( "LONGITUDE", -122.084095f); Log.d("GPSService", "lat/long: "+latitude+": "+longitude); return START_STICKY; }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-4 onDestroy()方法 • 在onDestroy()方法呼叫removeUpdates()方法移除定位服務的位置更新,如下所示: @Override public void onDestroy() { manager.removeUpdates(this); }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-5 onBind()方法 • onBind()方法並沒有使用,但是因為是抽象方法,類別一定要實作,所以傳回null,如下所示: @Override public IBinder onBind(Intent intent) { return null; }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-6 實作LocationListener介面的4個方法 • 因為GPSService服務類別本身是實作LocationListener介面的傾聽者物件,所以需要實作介面的4個方法,不過,我們只有使用onLocationChanged()方法,如下所示: @Override public void onLocationChanged(Location current) { if (current == null) return; Location dest = new Location(current); dest.setLatitude(latitude); dest.setLongitude(longitude); float distance = current.distanceTo(dest); Log.d("Ch14_3", "距離: " + distance); • 程式碼在建立目標座標的Location物件後,呼叫參數current目前Location物件的distanceTo()方法,參數是目標的Location物件,可以計算2個座標之間的距離是多少公尺。
14-3 本地服務與定位應用-GPS景點防撞雷達步驟四:建立GPSService服務類別-7 • 在下方if條件檢查距離是否小於100公尺,如果是,建立Intent物件送出廣播,如下所示: if (distance < 100.0) { if (isInArea == false) { Intent intent = new Intent( "android.broadcast.LOCATION"); sendBroadcast(intent); isInArea = true; } } else { isInArea = false; } }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟五:建立GPSReceiver廣播接收器類別 • GPSReceiver廣播接收器類別是用來接收GPSService服務送出的android.broadcast.LOCATION廣播,可以使用Toast類別顯示訊息,和振動提醒已經接近景點範圍,如下所示: public class GPSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "已經接近景點範圍", Toast.LENGTH_LONG).show(); Vibrator vibrator = (Vibrator) context.getSystemService( Context.VIBRATOR_SERVICE); vibrator.vibrate(500); // 半秒 } }
14-3 本地服務與定位應用-GPS景點防撞雷達步驟六:在AndroidManifest.xml註冊元件和新增權限 • GPSService服務和GPSReceiver廣播接收器需要在AndroidManifest.xml檔註冊,可以處理android.broadcast.LOCATION的廣播,如下所示: <receiver android:name=".GPSReceiver"> <intent-filter> <action android:name="android.broadcast.LOCATION" /> </intent-filter> </receiver> <service android:name=".GPSService"/> • GPS景點防撞雷達需要使用定位服務和振動,所以需要新增2個權限,如下所示: <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.VIBRATE"/>
14-4 使用Google Maps API-My地圖 • 14-4-1 取得Google Maps API金鑰 • 14-4-2 使用MapView元件和MapActivity類別-My地圖