第14章 Google地圖與定位服務 14-1 定位服務-我在哪裡 14-2 地圖解碼服務-找出景點座標

Slides:



Advertisements
Similar presentations
第一單元 建立java 程式.
Advertisements

第16章 首頁畫面小工具與硬體介面 16-1 首頁畫面小工具-手機靜音切換 16-2 感測器與遊戲控制-跳跳球遊戲
第13章 繪圖與多媒體 13-1 顯示圖檔-行動相簿 13-2 音樂播放-音樂播放器 13-3 影片播放-視訊播放器
第13章 繪圖與多媒體 13-1 顯示圖檔-行動相簿 13-2 音樂播放-音樂播放器 13-3 影片播放-視訊播放器
第15章 網路與通訊 15-1 WebView元件-行動瀏覽器 15-2 簡訊處理-我的簡訊 15-3 寄送電子郵件-郵件寄送工具
實驗五:多媒體播放器選單介面.
Part 2 開發Android應用程式的流程
位置與地圖應用 此投影片為講解Android如何取得定位經緯度和使用Google Map地圖.
第二章 JAVA语言基础.
Android + Web Service 建國科技大學 資管系 饒瑞佶 2017/3 V1.
ArrayAdapter & Spinner
期末專題報告 MyPosition - 我在何處報你知 組員:曾哲浩 蔡承翰 指導老師:陳朝鈞.
厦门大学数据库实验室 报告人:谢荣东 导师:林子雨 2014年8月30日
實驗四:單位轉換程式.
第2章 建立Android應用程式 2-1 Java語言、XML文件與Android 2-2 建立第一個Android應用程式
Chapter 13 Android 實戰演練.
Android + JUnit 單元測試 建國科技大學資管系 饒瑞佶 2012/8/19V4.
實驗十三:顯示目前經緯度位置.
Ch06 再談選單元件 物件導向系統實務.
JAVA vs. SQL Server 建國科技大學 資管系 饒瑞佶 2013/4 V1.
使用Android控制Arduino 史先强
Q101 在701 SDX Linux上的標準安裝與使用程序v2
第11章 Android GPS位置服务与地图编程
第9章 位置服务与地图应用.
第一个Android程序 本讲大纲: 1、创建Android应用程序 2、Android项目结构说明 3、运行Android应用程序
第8章 Android内容提供者(ContentProvider)应用
Android智慧型手機程式設計實務應用班
客戶端的檔案上傳 HtmlInputFile檔案控制項 上傳單一檔案 同時上傳多個檔案.
Ch5 Android應用程式的主要組成.
Chapter 7 Android應用元件 Android應用元件可以幫助我們獲得系統資源訊息(ActivityManager)、提供系統服務(Service)、搜尋系統服務(SearchManager)、監聽Intent訊息(Broadcast Receiver)以及資料共享(ContentProvider和ContentResolver)。
Android介面設計 Android智慧型手機程式設計 建國科技大學 資管系 饒瑞佶 2012/4 V1 2012/8 V2
第4章 Android生命周期.
第9章 使用意圖啟動活動與內建應用程式 9-1 意圖的基礎 9-2 使用意圖啟動活動
CH7 佈局、按鈕與文字編輯元件.
Android + Service 建國科技大學 資管系 饒瑞佶.
SQL Stored Procedure SQL 預存程序.
R教學 安裝RStudio 羅琪老師.
實驗十四:顯示與控制地圖.
第2讲 移动应用开发基础知识(二) 宋婕
第8章 Service解析.
第6章 建立Android使用介面 6-1 介面元件的基礎 6-2 Android的事件處理 6-3 按鈕元件 6-4 文字元件
Android App簡介及 App Inventor 2體驗 靜宜大學資管系 楊子青
生活智慧王 樹德科技大學 資訊工程系 指導教授 : 陳毓璋 教授 小組成員: 劉上緯 翁維廷 洪文財.
第10章 GPS位置服务与地图编程.
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
實驗十一:待辦事項程式 (儲存在手機上).
主编:钟元生 赵圣鲁.
Ch20. 計算器 (Mac 版本).
Android Application Component
實驗十五:標記目前位置.
實驗九:延續實驗八, 製作一個完整音樂播放器
Location Based Services - LBS
第二章 Java语法基础.
Android視窗介面 建國科技大學 資管系 饒瑞佶 2010/10.
第二章 Java基本语法 讲师:复凡.
進階UI元件:ListView元件以及複選 靜宜大學資管系 楊子青
國立台灣大學 關懷弱勢族群電腦課程 By 資訊工程 黃振修
Android Speech To Text(STT)
Activity的生命週期: 播放音樂與影片 靜宜大學資管系 楊子青
用Intent啟動程式中的其他Activity、運用WebView顯示網頁 靜宜大學資管系 楊子青
第2章 Java语言基础.
第9章 BroadcastReceiver的使用
第四組 停車場搜尋系統 第四組 溫允中 陳欣暉 蕭積遠 李雅俐.
加速感測器 靜宜大學資管系 楊子青.
NFC (近場通訊, Near Field Communication) 靜宜大學資管系 楊子青
SQLite資料庫 靜宜大學資管系 楊子青.
Part 8 Broadcast Receiver、Service和App Widget
方法(Method) 函數.
InputStreamReader Console Scanner
Presentation transcript:

第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 Studio專案 請啟動Android Studio開啟專案Ch14_1_2,內含1個Java類別檔和版面配置檔activity_main.xml。執行可以看到程式顯示目前的位置座標,按【顯示Google地圖】鈕,可以啟動Google地圖顯示此座標附近的地圖,即台北火車站,如下圖所示:

14-1-2 使用定位服務-我在哪裡 步驟二:建立我在哪裡使用介面的版面配置 使用介面的版面配置是定義在activity_main.xml檔,垂直編排1個TextView和Button元件,如下圖所示:

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; …... }

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)) {

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();

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); }

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-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();

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());

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)); }

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) { } };

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(); }

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); }

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"/>

14-2 地圖解碼服務-找出景點座標(說明) 地圖解碼服務(Geocoding Services)可以從位置名稱、郵遞區號等資訊來找出經緯度座標,或反過來,從經緯度座標找出位置名稱或地址。 Android是使用Geocoder類別來處理座標轉換,相關方法的說明,如下表所示: 方法 說明 getFromLocation() 將經緯度座標轉換成地址資訊,目前台灣只能轉換成所屬鄉鎮區和郵遞區號 getFromLocationName() 將位置名稱或地址轉換成經緯度座標

14-2 地圖解碼服務-找出景點座標 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch14_2,內含1個Java類別檔和版面配置檔activity_main.xml,其執行結果如下圖所示:

14-2 地圖解碼服務-找出景點座標 步驟二:建立找出景點座標使用介面的版面配置 找出景點座標的使用介面是定義在activity_main.xml檔,內含多個TextView、EditText元件和Button元件,如下圖所示:

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; … }

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); }

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物件,參數依序是緯度、經度和最多傳回的地址數。

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景點防撞雷達

14-3 本地服務與定位應用-GPS景點防撞雷達 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch14_3,內含3個Java類別檔和版面配置檔activity_main.xml,其執行結果如右圖所示:

14-3 本地服務與定位應用-GPS景點防撞雷達 步驟二:建立GPS景點防撞雷達使用介面的版面配置 GPS景點防撞雷達使用介面的版面配置是定義在activity_main.xml檔,內含多個TextView、EditText和Button元件,如下圖所示:

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; …... }

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); }

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()方法 @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;

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()方法移除定位服務的位置更新,並且使用try/catch處理使用者SecurityException沒有授權的例外,如下所示: @Override public void onDestroy() { try { manager.removeUpdates(this); } catch(SecurityException sex) { Log.e("Ch14_3", "GPS權限失敗..." + sex.getMessage());

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的廣播,如下所示: <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>

14-3 本地服務與定位應用-GPS景點防撞雷達 步驟六:在AndroidManifest.xml註冊元件和新增權限 因為需要使用定位服務和振動,所以新增2種權限,如下所示: <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.VIBRATE"/>

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的應用程式。

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地圖使用介面的設定類別,可以指定開啟的使用介面

14-4 使用Google Maps API-My地圖 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch14_4,內含1個Java類別檔和activity_maps.xml版面配置檔,其執行結果如下圖所示:

14-4 使用Google Maps API-My地圖 步驟二:使用Google Maps Activity範本建立專案 本節範例是直接使用【Google Maps Activity】活動範本建立地圖應用程式,我們是在Fragment片段顯示地圖,和在地圖上使用Marker類別作記號,如下圖所示:

14-4 使用Google Maps API-My地圖 步驟二:使用Google Maps Activity範本建立專案 預設建立名為MapsActivity活動類別,和activity_maps版面配置檔,按【Finish】鈕建立專案。

14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(說明) 在將Google地圖整合至Android應用程式之前,我們需要先上網取得免費Google Maps API金鑰。Android Studio專案的【Google Maps Activity】範本提供相關申請資訊的超連結,可以幫助我們更快速的申請Google Maps API金鑰,其步驟如下所示:

14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step1) Step 1:請展開專案「res\values」目錄,開啟【google_maps_api.xml】檔案,如下圖所示:

14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step2) Step 2:請複製反白行的超連結文字後,啟動瀏覽器進入此網頁,筆者是使用Google Chrome為例,如下圖所示:

14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step3) Step 3:在登入Google帳戶後,如果有看到最後2題問題,請在最後選【是】後,按【同意並繼續】鈕替專案啟用API,稍等一下,可以看到已啟用API頁面,如下圖所示:

14-4 使用Google Maps API-My地圖 步驟三:申請Google Maps API金鑰(Step4) Step 4:按【建立API金鑰】鈕建立Android金鑰,稍等一下,可以看到建立的API金鑰字串,請複製此金鑰,如下圖所示:

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>

14-4 使用Google Maps API-My地圖 步驟四:建立Fragment片段的版面配置 My地圖使用介面的版面配置是定義在activity_maps.xml檔,活動範本自動產生SupportMapFragment類別的<fragment>標籤,如下所示: <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:map="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/map" 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" />

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; …... }

14-4 使用Google Maps API-My地圖 步驟五:建立MapsActivity活動類別和使用定位服務2 onCreate()方法 onResume()方法 onPause()方法 setUpMap()方法 onMapReady()方法 實作LocationListener介面的內層類別

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" android:value="@string/google_maps_key" /> 上述<meta-data>標籤指定Google Maps API金鑰。

14-5 標記Google地圖-追蹤個人行蹤 Google Maps Android API可以讓我們使用MapView元件或MapFragment類別建立地圖應用程式,和在地圖上作記號,活用此功能,就可以建立多樣化應用,例如:標記附近的加油站、停車場位置或個人行蹤等。 在這一節我們準則建立追蹤個人行蹤的Android應用程式,第14-4節只有使用1個標記,在這一節我們準備在行動裝置的Google地圖上標記最近十次的位置座標。

14-5 標記Google地圖-追蹤個人行蹤 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch14_5,內含2個Java類別檔和2個版面配置檔,其執行結果如右圖所示:

14-5 標記Google地圖-追蹤個人行蹤 步驟二:建立主活動使用介面的版面配置 主活動使用介面的版面配置是定義在activity_main.xml檔案,依序編排2個TextView和Button元件,如下圖所示:

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]; …... }

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) { ...... }

14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-3 onResume()方法 onResume()方法和第14-1-2節步驟三的同名方法相同,只是更新位置頻率的條件不同,如下所示: int minTime = 5000; // 毫秒 float minDistance = 15; // 公尺

14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-4 onPause()方法 在覆寫onPause()方法呼叫removeUpdates()方法取消周期更新位置,此方法和第14-1-2節步驟三的同名方法相同。

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);

14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-6 LocationListener傾聽者物件 在requestLocationUpdates()方法需要註冊定位服務的傾聽者物件,即使用匿名內層類別實作LocationListener介面來建立此物件,此物件和第14-1-2節步驟三的同名物件相同。

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));

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) {

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();

14-5 標記Google地圖-追蹤個人行蹤 步驟三:建立Activity主活動類別記錄座標清單-10 button_Click()事件處理方法 button_Click()事件處理方法可以依據目前的位置座標來啟動Google地圖,它和第14-1-2節步驟三的同名方法相同。

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); }

14-5 標記Google地圖-追蹤個人行蹤 步驟四:新增Google Maps Activity範本的活動 這一節範例共有2個活動,請在專案「Project」視窗的【app\java】上,執行【右】鍵快顯功能表的「New/Google/Google Maps Activity」指令,可以看到新增活動的精靈畫面。

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; …... }

14-5 標記Google地圖-追蹤個人行蹤 步驟五:建立MapsActivity活動類別來標記地圖-2 onCreate()方法 setUpMap()方法 onMapReady()回撥方法

14-5 標記Google地圖-追蹤個人行蹤 步驟六:在AndroidManifest.xml註冊活動和新增權限 因為本節專案擁有2個活動,在使用活動範本新增第2個活動後,預設新增MapsActivity活動的<activity>標籤,如下所示: <activity android:name=".MapsActivity" android:label="@string/title_activity_maps"></activity> 其他部分是【Google Maps Activity】活動範本新增的權限,和使用<meta-data>標籤定義參數,這部分和第14-4節相同,筆者就不重複說明。

End