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

內容說明:GPS定位、網路定位

資料來源:Android南部工作坊、Google!Android 2手機應用程式設計入門第三版

Android提供兩種定位方式,一種是GPS,另一種是網路定位。前者用於室外;後者用於室內。

首先要建立一個專案,如果未來想結合Google服務,可勾選Google APIs,詳細說明請參考Google地圖

設定使用權限

開啟AndroidManifest.xml,並且在後面加入的權限。INTERNET是網路權限,ACCESS_FINE_LOCATION是GPS權限,ACCESS_COARSE_LOCATION是網路定位。

<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程式碼如下:

<resources>
    <string name="app_name">GPS/網路定位</string>
    <string name="longitude_label">經度:</string>
    <string name="latitude_label">緯度:</string>
</resources>

main.xml程式如下:

<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”實作介面,並且根據指示增加實作方法,產生的完整程式碼如下:

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行程式碼:

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

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」,表示如果系統只有啟用一個提供者,要不要使用它。

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。

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

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

@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()。

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

專案程式碼下載 (2014.05.14 新增)

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

下載連結請 按此