Download presentation
Presentation is loading. Please wait.
1
第14章 Google地圖與定位服務 14-1 定位服務-我在哪裡 14-2 地圖解碼服務-找出景點座標
14-3 本地服務與定位應用-GPS景點防撞雷達 14-4 使用Google Maps API-My地圖 14-5 標記Google地圖-追蹤個人行蹤
2
14-1 定位服務-我在哪裡 Android的定位服務與座標 使用定位服務-我在哪裡
3
14-1-1 Android的定位服務與座標-說明
Android行動裝置結合定位功能和Google地圖建立的「位置感知服務」(Location-based Service,LBS),這是一項十分實用的功能,LBS應用程式可以追蹤你的位置和提供一些額外服務,例如:找出附近的咖啡廳、停車場、自動櫃員機或加油站等。 Android作業系統提供LocationManager類別的定位服務來幫助我們存取行動裝置目前的定位資料,包含:緯度(Latitude)、經度(Longitude)和高度(Altitude)等。
4
14-1-1 Android的定位服務與座標-種類
GPS定位提供者:提供者名稱字串為"gps",它是使用GPS(Global Positioning System)的衛星訊號來定位,可以提供精確的位置資訊,但是無法收到衛星訊號的室內並無法使用。 網路定位提供者;提供者名稱字串為"network",它是直接使用電信公司基地台來執行三角定位,其提供的位置資訊較不精確,但是可以在室內使用。
5
14-1-1 Android的定位服務與座標-座標
定位服務最主要的目的是找出行動裝置目前位置的經緯度座標,經緯度是經度與緯度合稱的座標系統,也稱為地理座標系統,它是使用三度空間的球面來定義地球表面各點的座標系統,能夠標示地球表面上的任何一個位置。經度與緯度的說明,如下所示: 緯度:地球表面某一點距離地球赤道以南或以北的度數,其值為0至90度,赤道以北的緯度叫北緯(符號為N);赤道以南的緯度稱南緯(符號為S)。 經度:地球表面上某一點距離本初子午線(一條南北方向經過倫敦格林威治天文台舊址的子午線)以東或以西的度數,簡單的說,本初子午線的經度是0度,其他地點的經度是向東從0到180度,即東經(符號為W)或向西從0到180度,即西經(符號為E)。
6
使用定位服務-我在哪裡(說明) 我在哪裡是定位服務的最簡單應用,可以顯示目前行動裝置的經緯度座標。
7
14-1-2 使用定位服務-我在哪裡 步驟一:開啟和執行Android Studio專案
請啟動Android Studio開啟專案Ch14_1_2,內含1個Java類別檔和版面配置檔activity_main.xml。執行可以看到程式顯示目前的位置座標,按【顯示Google地圖】鈕,可以啟動Google地圖顯示此座標附近的地圖,即台北火車站,如下圖所示:
8
14-1-2 使用定位服務-我在哪裡 步驟二:建立我在哪裡使用介面的版面配置
使用介面的版面配置是定義在activity_main.xml檔,垂直編排1個TextView和Button元件,如下圖所示:
9
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-1
在MainActivity活動類別的開頭宣告成員的LocationManager和Location物件變數,如下所示: public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 101; private LocationManager manager; private Location currentLocation; private String best; …... }
10
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-2
onCreate()方法 在覆寫onCreate()方法載入版面配置後,呼叫getSystemService()方法取得系統服務LocationManager物件,if條件判斷是否啟用GPS,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); manager = (LocationManager)getSystemService( LOCATION_SERVICE); if (!manager.isProviderEnabled( LocationManager.GPS_PROVIDER)) {
11
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-3
if條件呼叫LocationManager物件的isProviderEnabled()方法檢查是否啟用GPS,沒有,就顯示對話方塊要求啟用GPS,如下所示: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("定位管理") .setMessage("GPS目前狀態是尚未啟用.\n" +"請問你是否現在就設定啟用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();
12
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-4
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{ Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); }
13
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-5
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
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-6
manager.requestLocationUpdates(best, minTime, minDistance, listener); } else { currentLocation = manager.getLastKnownLocation( LocationManager.GPS_PROVIDER); manager.requestLocationUpdates( LocationManager.GPS_PROVIDER, minTime, minDistance, listener); updatePosition();
15
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-7
onPause()方法 在覆寫onPause()方法呼叫removeUpdates()方法取消周期更新位置,如下所示: @Override protected void onPause() { super.onPause(); try { manager.removeUpdates(listener); } catch(SecurityException sex) { Log.e("Ch14_1_2", "GPS權限失敗..." + sex.getMessage());
16
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-8
updatePosition()方法 在自訂updatePosition()方法更新TextView元件顯示的位置資訊,如下所示: private void updatePosition() { TextView output; output = (TextView) findViewById(R.id.output); if (currentLocation == null) { output.setText("取得定位資訊中..."); } else { output.setText(getLocationInfo(currentLocation)); }
17
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-9
LocationListener傾聽者物件 使用匿名內層類別實作LocationListener介面來建立此物件,需要實作4個方法,如下所示: private LocationListener listener = new LocationListener() { @Override public void onLocationChanged(Location location) { currentLocation = location; updatePosition(); } public void onProviderDisabled(String provider) { } public void onProviderEnabled(String provider) { } public void onStatusChanged(String provider, int status, Bundle extras) { } };
18
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-10
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(); }
19
14-1-2 使用定位服務-我在哪裡 步驟三:建立Activity活動類別使用定位服務-11
button_Click()事件處理方法 button_Click()事件處理方法可以依據目前的位置座標來啟動Google地圖,它是使用Intent物件啟動Google地圖程式,首先取得經緯度座標longitude和latitude,如下所示: public void button_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); }
20
14-1-2 使用定位服務-我在哪裡 步驟四:在AndroidManifest.xml新增權限
因為需要使用定位服務,所以在AndroidManifest.xml檔新增2個權限,ACCESS_COURSE_LOCATION是網路定位服務,ACCESS_FINE_LOCATION是GPS定位服務,如下所示: <uses-permission android:name= "android.permission.ACCESS_COARSE_LOCATION"/> "android.permission.ACCESS_FINE_LOCATION"/>
21
14-2 地圖解碼服務-找出景點座標(說明) 地圖解碼服務(Geocoding Services)可以從位置名稱、郵遞區號等資訊來找出經緯度座標,或反過來,從經緯度座標找出位置名稱或地址。 Android是使用Geocoder類別來處理座標轉換,相關方法的說明,如下表所示: 方法 說明 getFromLocation() 將經緯度座標轉換成地址資訊,目前台灣只能轉換成所屬鄉鎮區和郵遞區號 getFromLocationName() 將位置名稱或地址轉換成經緯度座標
22
14-2 地圖解碼服務-找出景點座標 步驟一:開啟和執行Android Studio專案
請啟動Android Studio開啟專案Ch14_2,內含1個Java類別檔和版面配置檔activity_main.xml,其執行結果如下圖所示:
23
14-2 地圖解碼服務-找出景點座標 步驟二:建立找出景點座標使用介面的版面配置
找出景點座標的使用介面是定義在activity_main.xml檔,內含多個TextView、EditText元件和Button元件,如下圖所示:
24
14-2 地圖解碼服務-找出景點座標 步驟三:建立Activity活動類別找出景點座標-1
在MainActivity活動類別的開頭宣告成員的ArrayAdapter和Geocoder等物件變數,如下所示: public class MainActivity extends AppCompatActivity { 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; … }
25
14-2 地圖解碼服務-找出景點座標 步驟三:建立Activity活動類別找出景點座標-2
onCreate()方法 在覆寫的onCreate()方法載入版面配置後,可以取得EditText物件來取得輸入座標,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_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); }
26
14-2 地圖解碼服務-找出景點座標 步驟三:建立Activity活動類別找出景點座標-3
button_Click()事件處理方法 在button_Click()事件處理方法將經緯度座標轉換成地址,首先將經緯度的輸入字串轉換成float浮點數,如下所示: public void button_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物件,參數依序是緯度、經度和最多傳回的地址數。
27
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();
28
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());
29
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個參數最多傳回幾個座標。
30
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());
31
14-2 地圖解碼服務-找出景點座標 步驟三:建立Activity活動類別找出景點座標-8
button3_Click()事件處理方法 button3_Click()事件處理方法使用Intent物件啟動Google地圖,它和第14-1-2節的button1_Click()事件處理方法相同,筆者就不重複說明。
32
14-2 地圖解碼服務-找出景點座標 步驟四:在AndroidManifest.xml新增存取Internet權限
找出景點座標因為需要連線Internet,所以在AndroidManifest.xml檔需要新增INTERNET權限,如下所示: <uses-permission android:name="android.permission.INTERNET"/>
33
14-3 本地服務與定位應用-GPS景點防撞雷達
34
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟一:開啟和執行Android Studio專案
請啟動Android Studio開啟專案Ch14_3,內含3個Java類別檔和版面配置檔activity_main.xml,其執行結果如右圖所示:
35
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟二:建立GPS景點防撞雷達使用介面的版面配置
GPS景點防撞雷達使用介面的版面配置是定義在activity_main.xml檔,內含多個TextView、EditText和Button元件,如下圖所示:
36
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟三:建立Activity活動類別的事件處理方法-1
在MainActivity活動類別的開頭宣告成員的EditText和TextView物件變數,如下所示: public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 101; private EditText lat, lon; private TextView output; …... }
37
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟三:建立Activity活動類別的事件處理方法-2
onCreate()方法 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lat = (EditText) findViewById(R.id.txtLat); lon = (EditText) findViewById(R.id.txtLong); output = (TextView) findViewById(R.id.lblOutput); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{ Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); }
38
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("服務啟動中..."); }
39
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("服務停止中..."); }
40
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟三:建立Activity活動類別的事件處理方法-5
finish_Click()事件處理方法 在finish_Click()事件處理方法呼叫finish()方法結束活動,如下所示: public void finish_Click(View view) { finish(); }
41
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; …. }
42
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟四:建立GPSService服務類別-2
onCreate()方法 @Override public void onCreate() { manager = (LocationManager) getSystemService(LOCATION_SERVICE); try { manager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 10000, 1, this); } catch(SecurityException sex) { Log.e("Ch14_3", "GPS權限失敗..." + sex.getMessage()); isInArea = false;
43
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", f); longitude = (double) intent.getFloatExtra( "LONGITUDE", f); Log.d("GPSService", "lat/long: "+latitude+": "+longitude); return START_STICKY; }
44
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟四:建立GPSService服務類別-4
onDestroy()方法 在onDestroy()方法呼叫removeUpdates()方法移除定位服務的位置更新,並且使用try/catch處理使用者SecurityException沒有授權的例外,如下所示: @Override public void onDestroy() { try { manager.removeUpdates(this); } catch(SecurityException sex) { Log.e("Ch14_3", "GPS權限失敗..." + sex.getMessage());
45
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟四:建立GPSService服務類別-5
onBind()方法 onBind()方法並沒有使用,但是因為是抽象方法,類別一定要實作,所以傳回null,如下所示: @Override public IBinder onBind(Intent intent) { return null; }
46
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個座標之間的距離是多少公尺。
47
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;
48
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); // 半秒 }
49
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟六:在AndroidManifest.xml註冊元件和新增權限
GPSService服務和GPSReceiver廣播接收器需要在AndroidManifest.xml檔註冊,可以處理android.broadcast.LOCATION的廣播,如下所示: <service android:name=".GPSService" android:enabled="true" android:exported="true" > </service> <receiver android:name=".GPSReceiver" <intent-filter> <action android:name="android.broadcast.LOCATION" /> </intent-filter> </receiver>
50
14-3 本地服務與定位應用-GPS景點防撞雷達 步驟六:在AndroidManifest.xml註冊元件和新增權限
因為需要使用定位服務和振動,所以新增2種權限,如下所示: <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.VIBRATE"/>
51
14-4 使用Google Maps API-My地圖(說明)
Google Map(地圖)在第9-4-2節已經說明過如何使用Intent物件啟動Google地圖,和顯示指定經緯度座標附近的地圖。 實務上,我們可能需要將Google地圖內嵌Android應用程式之中,此時我們需要使用Google Maps Android API。請注意!我們需要取得Google Maps API金鑰,和在Android模擬器選Google API,才能在Android模擬器上執行Google Maps Android API的應用程式。
52
14-4 使用Google Maps API-My地圖(說明)
Google Maps Android API核心類別的簡單說明,如下表所示: 類別 說明 GoogleMap Google Maps Android API的主要類別,負責下載、顯示地圖和回應地圖操作,我們不能直接建立此物件,需要從MapView和MapFragment物件呼叫getMap()方法來取得 MapView 一種View元件,可以下載和顯示Google地圖 SupportFragmentMap 建立擁有Google地圖的Fragment片段 Marker 此類別可以在Google地圖上顯示標記 UiSettings Google地圖使用介面的設定類別,可以指定開啟的使用介面
53
14-4 使用Google Maps API-My地圖 步驟一:開啟和執行Android Studio專案
請啟動Android Studio開啟專案Ch14_4,內含1個Java類別檔和activity_maps.xml版面配置檔,其執行結果如下圖所示:
54
14-4 使用Google Maps API-My地圖 步驟二:使用Google Maps Activity範本建立專案
本節範例是直接使用【Google Maps Activity】活動範本建立地圖應用程式,我們是在Fragment片段顯示地圖,和在地圖上使用Marker類別作記號,如下圖所示:
55
14-4 使用Google Maps API-My地圖 步驟二:使用Google Maps Activity範本建立專案
預設建立名為MapsActivity活動類別,和activity_maps版面配置檔,按【Finish】鈕建立專案。
56
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(說明)
在將Google地圖整合至Android應用程式之前,我們需要先上網取得免費Google Maps API金鑰。Android Studio專案的【Google Maps Activity】範本提供相關申請資訊的超連結,可以幫助我們更快速的申請Google Maps API金鑰,其步驟如下所示:
57
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step1)
Step 1:請展開專案「res\values」目錄,開啟【google_maps_api.xml】檔案,如下圖所示:
58
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step2)
Step 2:請複製反白行的超連結文字後,啟動瀏覽器進入此網頁,筆者是使用Google Chrome為例,如下圖所示:
59
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step3)
Step 3:在登入Google帳戶後,如果有看到最後2題問題,請在最後選【是】後,按【同意並繼續】鈕替專案啟用API,稍等一下,可以看到已啟用API頁面,如下圖所示:
60
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step4)
Step 4:按【建立API金鑰】鈕建立Android金鑰,稍等一下,可以看到建立的API金鑰字串,請複製此金鑰,如下圖所示:
61
14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step5)
Step 5:回到Android Studio,將複製的API金鑰字串貼至google_maps_api.xml檔的YOUR_KEY_HERE位置,如下所示: <string name="google_maps_key" templateMergeStrategy="preserve">YOUR_KEY_HERE</string>
62
14-4 使用Google Maps API-My地圖 步驟四:建立Fragment片段的版面配置
My地圖使用介面的版面配置是定義在activity_maps.xml檔,活動範本自動產生SupportMapFragment類別的<fragment>標籤,如下所示: <fragment xmlns:android=" xmlns:map=" xmlns:tools=" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.joe.ch14_4.MapsActivity" />
63
14-4 使用Google Maps API-My地圖 步驟五:建立MapsActivity活動類別和使用定位服務1
MapsActivity活動類別是一個擁有Fragment片段的活動,類似ListView元件,我們是繼承FragmentActivity類別建立活動和實作OnMapReadyCallback介面,首先宣告定位服務的物件變數,之後是Google地圖的GoogleMap和標記Marker物件變數,如下所示: public class MapsActivity extends FragmentActivity implements OnMapReadyCallback { private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 101; private LocationManager manager; private LocationListener locationListener; private GoogleMap mMap; private Marker marker; …... }
64
14-4 使用Google Maps API-My地圖 步驟五:建立MapsActivity活動類別和使用定位服務2
onCreate()方法 onResume()方法 onPause()方法 setUpMap()方法 onMapReady()方法 實作LocationListener介面的內層類別
65
14-4 使用Google Maps API-My地圖 步驟六:在AndroidManifest.xml新增權限
Android Studio的【Google Maps Activity】活動範本會自動在AndroidManifest.xml檔產生ACCESS_FINE_LOCATION權限,如下所示: <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION" /> 上述權限不是Google地圖所需權限,而是定位服務的權限。在<application>標籤新增<meta-data>子標籤,如下所示: <meta-data android:name="com.google.android.geo.API_KEY" /> 上述<meta-data>標籤指定Google Maps API金鑰。
66
14-5 標記Google地圖-追蹤個人行蹤 Google Maps Android API可以讓我們使用MapView元件或MapFragment類別建立地圖應用程式,和在地圖上作記號,活用此功能,就可以建立多樣化應用,例如:標記附近的加油站、停車場位置或個人行蹤等。 在這一節我們準則建立追蹤個人行蹤的Android應用程式,第14-4節只有使用1個標記,在這一節我們準備在行動裝置的Google地圖上標記最近十次的位置座標。
67
14-5 標記Google地圖-追蹤個人行蹤 步驟一:開啟和執行Android Studio專案
請啟動Android Studio開啟專案Ch14_5,內含2個Java類別檔和2個版面配置檔,其執行結果如右圖所示:
68
14-5 標記Google地圖-追蹤個人行蹤 步驟二:建立主活動使用介面的版面配置
主活動使用介面的版面配置是定義在activity_main.xml檔案,依序編排2個TextView和Button元件,如下圖所示:
69
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-1
在MainActivity活動類別的開頭宣告常數與成員變數,使用Lats[]和Lngs[]陣列儲存個人行蹤的GPS座標,如下所示: public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 101; private final int MAX_RECORDS = 10; private LocationManager manager; private Location currentLocation; private int index = 0, count = 0; private double[] Lats = new double[MAX_RECORDS]; private double[] Lngs = new double[MAX_RECORDS]; …... }
70
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-2
onCreate()方法 在覆寫onCreate()方法載入版面配置後,取得系統服務的LocationManager物件,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); manager = (LocationManager) getSystemService(LOCATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ...... }
71
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-3
onResume()方法 onResume()方法和第14-1-2節步驟三的同名方法相同,只是更新位置頻率的條件不同,如下所示: int minTime = 5000; // 毫秒 float minDistance = 15; // 公尺
72
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-4
onPause()方法 在覆寫onPause()方法呼叫removeUpdates()方法取消周期更新位置,此方法和第14-1-2節步驟三的同名方法相同。
73
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-5
updatePosition()方法 在自訂updatePosition()方法更新TextView元件顯示的位置資訊和個人行蹤的座標清單,如下所示: @Override private void updatePosition() { TextView output, list; String str = "最近個人行蹤的座標清單:\n"; output = (TextView) findViewById(R.id.output); list = (TextView) findViewById(R.id.list); if (currentLocation == null) { output.setText("取得定位資訊中..."); } else { output.setText(getLocationInfo(currentLocation)); for (int i = 0; i < MAX_RECORDS; i++) { if (GPLat[i] != 0.0) str += GPLat[i] + "/" + GPLng[i] +"\n"; } list.setText(str);
74
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-6
LocationListener傾聽者物件 在requestLocationUpdates()方法需要註冊定位服務的傾聽者物件,即使用匿名內層類別實作LocationListener介面來建立此物件,此物件和第14-1-2節步驟三的同名物件相同。
75
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-7
getLocationInfo()方法 在自訂getLocationInfo()方法可以從參數Loaction物件取得定位資訊和檢查是否需要儲存個人行蹤的座標,如下所示: public String getLocationInfo(Location location) { boolean isSave = true; double lat, lng; lat = location.getLatitude(); lng = location.getLongitude(); StringBuffer str = new StringBuffer(); str.append("定位提供者(Provider): "+ location.getProvider()); str.append("\n緯度(Latitude): " + Double.toString(lat)); str.append("\n經度(Longitude): " + Double.toString(lng));
76
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-8
如果是,就可以計算第2個座標和前一個座標的距離,首先建立目地GPS座標的Location物件,如下所示: if (count >= 1) { Location dest = new Location(location); int preIndex = index - 1; if (preIndex < 0 ) preIndex = GPLat.length - 1; dest.setLatitude(GPLat[preIndex]); dest.setLongitude(GPLng[preIndex]); 在下方使用distanceTo()方法計算與目地座標的距離,如果距離小於20公尺就不儲存,此時旗標變數isSave的值為false,如下所示: float distance = location.distanceTo(dest); Toast.makeText(this, "距離: " + distance + "公尺", Toast.LENGTH_SHORT).show(); if (distance < 20.0) isSave = false; } if (isSave) {
77
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-9
上述if條件的isSave旗標變數值為true,表示需要新增座標,count變數記錄共儲存幾個座標,如果超過陣列尺寸,就指定為陣列尺寸的個數,如下所示: GPLat[index] = lat; GPLng[index] = lng; count++; if (count >= MAX_RECORDS) count = MAX_RECORDS; index++; if (index >= MAX_RECORDS) index = 0; } return str.toString();
78
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-10
button_Click()事件處理方法 button_Click()事件處理方法可以依據目前的位置座標來啟動Google地圖,它和第14-1-2節步驟三的同名方法相同。
79
14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-11
button2_Click()事件處理方法 button2_Click()事件處理方法可以在MapView元件顯示個人行蹤,這是使用Intent物件啟動MyMapActivity活動類別,附件是個人行蹤座標的2個陣列,和座標數的count變數值,如下所示: public void button2_Click(View view) { Intent mapView = new Intent(this, MyMapActivity.class); mapView.putExtra("GPSLATITUDE", GPLat); mapView.putExtra("GPSLONGITUDE", GPLng); mapView.putExtra("MAX_INDEX", count); startActivity(mapView); }
80
14-5 標記Google地圖-追蹤個人行蹤 步驟四:新增Google Maps Activity範本的活動
這一節範例共有2個活動,請在專案「Project」視窗的【app\java】上,執行【右】鍵快顯功能表的「New/Google/Google Maps Activity」指令,可以看到新增活動的精靈畫面。
81
14-5 標記Google地圖-追蹤個人行蹤 步驟五:建立MapsActivity活動類別來標記地圖-1
MapsActivity活動類別是繼承FragmentActivity類別,並且在類別開頭宣告GoogleMap物件變數,和儲存座標的陣列和尺寸,如下所示: public class MapsActivity extends FragmentActivity implements OnMapReadyCallback { private GoogleMap mMap; private double[] Lats, Lngs; private int max_index; …... }
82
14-5 標記Google地圖-追蹤個人行蹤 步驟五:建立MapsActivity活動類別來標記地圖-2
onCreate()方法 setUpMap()方法 onMapReady()回撥方法
83
14-5 標記Google地圖-追蹤個人行蹤 步驟六:在AndroidManifest.xml註冊活動和新增權限
因為本節專案擁有2個活動,在使用活動範本新增第2個活動後,預設新增MapsActivity活動的<activity>標籤,如下所示: <activity android:name=".MapsActivity" 其他部分是【Google Maps Activity】活動範本新增的權限,和使用<meta-data>標籤定義參數,這部分和第14-4節相同,筆者就不重複說明。
84
End
Similar presentations