Android開發筆記-GPS與網路定位

內容說明:GPS定位、網路定位
資料來源:Android南部工作坊、Google!Android 2手機應用程式設計入門第三版

Android提供兩種定位方式,一種是GPS,另一種是網路定位。前者用於室外;後者用於室內。
首先要建立一個專案,如果未來想結合Google服務,可勾選Google APIs,詳細說明請參考Google地圖

設定使用權限
開啟AndroidManifest.xml,並且在</application>後面加入<uses-permission...>的權限。INTERNET是網路權限,ACCESS_FINE_LOCATION是GPS權限,ACCESS_COARSE_LOCATION是網路定位。

17
18
19
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>

加入顯示欄位
為了使定位的結果清楚可見,可以先在設定介面中加入變數與欄位,方便顯示定位的結果。本範例的string.xml程式碼如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">GPS/網路定位</string>
    <string name="longitude_label">經度:</string>
    <string name="latitude_label">緯度:</string>
</resources>

main.xml程式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/longitude_label"
    />
<TextView  android:id="@+id/longitude"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=""
    />
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/latitude_label"
    />
<TextView  android:id="@+id/latitude"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=""
    />
</LinearLayout>

實作LocationListener介面
當定位座標改變時,我們希望系統也會自動抓取新的位置,所以必需使用LocationListener來傾聽位置的變化。開啟Java檔案,在class後面加入"implements LocationListener"實作介面,並且根據指示增加實作方法,產生的完整程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package moke.test;
 
import android.app.Activity;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
 
public class main extends Activity implements LocationListener {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}
 
	@Override
	public void onLocationChanged(Location arg0) {	//當地點改變時
		// TODO Auto-generated method stub
 
	}
 
	@Override
	public void onProviderDisabled(String arg0) {	//當GPS或網路定位功能關閉時
		// TODO Auto-generated method stub
 
	}
 
	@Override
	public void onProviderEnabled(String arg0) {	//當GPS或網路定位功能開啟
		// TODO Auto-generated method stub
 
	}
 
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {	//定位狀態改變
		//status=OUT_OF_SERVICE 供應商停止服務
		//status=TEMPORARILY_UNAVAILABLE 供應商暫停服務
	}
}

使用LocationManager取得位置
無論是GPS或網路定位,我們都可以利用LocationManager輕易地幫使用者定位。首先我們要先確定使用者有開啟定位服務,因此請在onCreate()加入21~29行程式碼:

17
18
19
20
21
22
23
24
25
26
27
28
29
30
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
 
		//取得系統定位服務
		LocationManager status = (LocationManager) (this.getSystemService(Context.LOCATION_SERVICE));
		if (status.isProviderEnabled(LocationManager.GPS_PROVIDER) || status.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
			//如果GPS或網路定位開啟,呼叫locationServiceInitial()更新位置
			locationServiceInitial();
		} else {
			Toast.makeText(this, "請開啟定位服務", Toast.LENGTH_LONG).show();
			startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));	//開啟設定頁面
		}
	}

如果未來需要開啟系統其它的設定頁面,可以參考官方網站。接著,我們就要來實作locationServiceInitial()方法:

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	private LocationManager lms;
	private void locationServiceInitial() {
		lms = (LocationManager) getSystemService(LOCATION_SERVICE);	//取得系統定位服務
		Location location = lms.getLastKnownLocation(LocationManager.GPS_PROVIDER);	//使用GPS定位座標
		getLocation(location);
	}
	private void getLocation(Location location) {	//將定位資訊顯示在畫面中
		if(location != null) {
			TextView longitude_txt = (TextView) findViewById(R.id.longitude);
			TextView latitude_txt = (TextView) findViewById(R.id.latitude);
 
			Double longitude = location.getLongitude();	//取得經度
			Double latitude = location.getLatitude();	//取得緯度
 
			longitude_txt.setText(String.valueOf(longitude));
			latitude_txt.setText(String.valueOf(latitude));
		}
		else {
			Toast.makeText(this, "無法定位座標", Toast.LENGTH_LONG).show();
		}
	}

取得最佳資訊提供者
Criteria物件是用來讓我們設定資訊提供者選取的標準。我們可以透過它設定精準度、電力、高度、速度、方位和金錢成本等因素作為選擇提供者的考量。locationServiceInitial()的修改如下。當我們在第36行建立一個新的Criteria物件時,因為沒有指定任何條件,所以選取提供者的準則就是沒有標準,誰都可以。到第37行時,使用getBestProvider來取得最佳提供者。如果候選人有很多位,它就會選擇精準度最高的;如果沒有符合標準的候選人,它就會依序根據電力、精準度、方位、速度或高度來選擇提供者。getBestProvider第二個參數一般都是「true」,表示如果系統只有啟用一個提供者,要不要使用它。

32
33
34
35
36
37
38
39
40
	private LocationManager lms;
	private String bestProvider = LocationManager.GPS_PROVIDER;	//最佳資訊提供者
	private void locationServiceInitial() {
		lms = (LocationManager) getSystemService(LOCATION_SERVICE);	//取得系統定位服務
		Criteria criteria = new Criteria();	//資訊提供者選取標準
		bestProvider = lms.getBestProvider(criteria, true);	//選擇精準度最高的提供者
		Location location = lms.getLastKnownLocation(bestProvider);
		getLocation(location);
	}

更新地點
在更新地點之前,我們必需先設定一個boolean getService參數,判斷定位服務是否已經開啟,這樣當使用者沒有開啟任何定位服務的時候,系統才不會出現錯誤。首先在onCreate()上方宣告一個boolean變數,並且在確定服務開啟時設定為true。

17
	private boolean getService = false;		//是否已開啟定位服務
25
26
27
28
29
		if (status.isProviderEnabled(LocationManager.GPS_PROVIDER) || status.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
			//如果GPS或網路定位開啟,呼叫locationServiceInitial()更新位置
			getService = true;	//確認開啟定位服務
			locationServiceInitial();
		...

接著,我們在onResume()開啟更新,並設定更新的頻率(為了讓我們在測式時快速有反應,所以設定成每秒更新1次,但快速的更新是很耗電的,實際使用時可以根據需求來設定)。當系統進入onPause()時,則停止更新(onResume與onPause請參考Android生命周期)。

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		if(getService) {
			lms.requestLocationUpdates(bestProvider, 1000, 1, this);
			//服務提供者、更新頻率60000毫秒=1分鐘、最短距離、地點改變時呼叫物件
		}
	}
 
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		if(getService) {
			lms.removeUpdates(this);	//離開頁面時停止更新
		}
	}

最後,我們希望畫面顯示的內容會隨著經緯度變化,所以請直接在onLocationChanged加入getLocation()。

79
80
81
82
83
	@Override
	public void onLocationChanged(Location location) {	//當地點改變時
		// TODO Auto-generated method stub
		getLocation(location);
	}

專案程式碼下載 (2014.05.14 新增)
由於來信索取程式碼的人很多,還是決定直接放上來提供下載。必須要提醒當初撰寫的版本是Android 2.3,現在已經有更新更好的寫法,並不建議使用。

下載連結請 按此

Share on Google+
You can follow any responses to this entry through the RSS 2.0 feed.You can leave a response, or trackback from your own site.
99 Responses
  1. WIND says:

    你好我想請問一下
    我有建好一個map
    我想要有一個按鈕
    當我在map上面隨意移動位置的時候
    點那個botton可以直接讓我在地圖上回到手機的現在位置
    請問要怎麼做了
    因為我普遍看到都是可以回到指定給的經緯度
    可是我手機的位置可能會因為我人移動而改變
    所以想請問這應該怎麼做修改
    謝謝

    • Davina says:

      顯示地圖的話應該用的是Google地圖,利用地圖定位就可以取得目前的位置(第36行mylayer.getMyLocation()就是目前位置)。
      不過這段程式碼是舊版的,新版的我還沒有研究,或許你可以在網路上找到相關的資訊(舊版本做得到的事新版沒理由做不到)。

      • 小蒼 says:

        您好 請問一下getLocation(location);
        這個部分要怎麼將座標傳給map

        我這邊怎麼測試都沒有辦法
        能否請大師講解

        感謝您

        • Davina says:

          這個部份你要去查map的方法,看要如何傳入經緯度,上面43-44行的getLongitude()和getLatitude()就是取得經緯度。
          我不知道你使用的map是那一個版本,v1的文章因為過時所以我拿掉了,v2我沒深入研究,只寫了基本的建立方法。
          我查了一下v2的文件,它有寫到設定地標的方式是:map.addMarker( new MarkerOptions().position( new LatLng(0, 0) ).title("Marker") );
          細節你要再試試。
          不過理論上map本身應該也能抓得到自己的座標才對(不需要額外寫GPS的程式)。

  2. 張竣皓 says:

    如果顯示"無法定位座標"
    該怎麼辦?

    儘管指教,謝謝

  3. wysk says:

    您好~很感謝程式分享,終於讓我發現我之前定位程式問題在哪!
    不知道你有沒有發現一個問題
    這個程式只要有開啟定位就會顯示值,既使你現在收不到GPS,它會呈現之前收到的最後一筆資料
    有沒有辦法讓它突破這個盲點?
    而你的設定位GPS是最佳提供者,能不能讓它在GPS開啟但收不到訊息時改用網路, 我也一值在嘗試,不過好像因為if,else if的優先順序讓它回不來。

    再次感謝,希望與一同參詳~!

    • Davina says:

      有個 onStatusChanged(String provider, int status, Bundle extras) 是"當定位狀態改變時"會呼叫,我想可以符合你的需求。
      當資訊提供者改變或無法取得位置時都會執行這個函式,status=OUT_OF_SERVICE 表示供應商停止服務,status=TEMPORARILY_UNAVAILABLE 表示供應商暫停服務。
      或許你可以在 onStatusChanged() 裡呼叫 locationServiceInitial(),讓它重新計算最佳資訊提供者。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *


一 + = 10

你可以使用這些 HTML 標籤與屬性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>