2016-06-16

【Android Studio】取得自己的 Android 裝置的定位

參考資料 ----
Getting the Last Known Location
GoogleApiClient
android-play-location - samples
Table 1. Individual APIs and corresponding build.gradle descriptions.

要取得定位,必須用到 Google Play Services,所以先檢查 SDK Manager,是否已安裝 Google Play Services



並且使用的 Android 實體裝置必須在 2.3.3(API 10) 以上,並且有安裝 Google Play
模擬器則須在 4.2.2(API 17) 以上(在模擬器測試操作頗麻煩,得先執行一次 Google 地圖,再執行自己的 app,才會顯示定位數字,所以建議在實體 Android 裝置測試、練習)


要引用 Google Play Services API,編輯 "程式" 層級的 build.gradle,加入

compile 'com.google.android.gms:play-services-location:8.1.0'

存檔,並按一下 工具列Sync Project with Gradle Files

如下圖:




插花小提示:
Google Play Services API 的版本數字不用太新
有時太新反而會因模擬器內建的版本跟不上,造成 app 無法正常執行 Google Play Services API,以這次練習而言,上圖就是我原本設定的版本數 9.0.2,在模擬器執行時,app 本身表面看起來是正常運行的,但 Android Monitor 會顯示如下圖的訊息,所以我就降成 8.1.0。



至於版本數字要設什麼,請看一下

\sdk 安裝目錄\extras\google\m2repository\com\google\android\gms\play-services-location
(各人安裝設定不同,如果您的目錄位置和我的不一樣,那可能得自己找找了...)

這裡存放著您有 Google Play Services API 的各個版本,每個版本一個目錄

 



注意:
儘量不要加入所有的 Google Play Services API,也就是說,不要寫下面這種形式 ----

compile 'com.google.android.gms:play-services:8.1.0'

這樣很容易超出 65K 限制,在您按下 Sync Project with Gradle Files 時會回應錯誤,建議只加入您所需要的 Google Play Services


開放權限
 
<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    ... >

    ...
    ...

    <!-- 精確定位,就是 GPS -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 約略定位,就是 WI-FI -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    ...
    ...
 

設定 activity_main.xml 版面
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- 經度  -->
    <TextView
        android:id="@+id/longitude_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:textIsSelectable="true" />

    <!-- 緯度  -->
    <TextView
        android:id="@+id/latitude_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:textIsSelectable="true" />

</LinearLayout>
 

GoogleApiClient 提供了 BuilderConnectionCallbacksOnConnectionFailedListener 兩個 Interface,所以我們要以 Builder 建立 GoogleApiClient 這個 instance 和 實做 ConnectionCallbacksOnConnectionFailedListener


MainActivity.java
 
...
...

// 記得 import 下面這幾個
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationServices;

public class LocationMainActivity extends AppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener
{
    protected static final String TAG = "MainActivity";

    protected GoogleApiClient mGoogleApiClient;
    protected Location mLastLocation;

    protected String mLatitudeLabel;
    protected String mLongitudeLabel;
    protected TextView mLatitudeText;
    protected TextView mLongitudeText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_location_main);

        mLatitudeLabel = "緯度";
        mLongitudeLabel = "經度";
        mLatitudeText = (TextView) findViewById((R.id.latitude_text));
        mLongitudeText = (TextView) findViewById((R.id.longitude_text));

        buildGoogleApiClient();
    }

    protected synchronized void buildGoogleApiClient()
    {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    protected void onStart()
    {
        super.onStart();
        mGoogleApiClient.connect();
    }

    @Override
    protected void onStop() 
    {
        super.onStop();
        if (mGoogleApiClient.isConnected()) 
        {
            mGoogleApiClient.disconnect();
        }
    }


    // 當 GoogleApiClient 連上 Google Play Service 後要執行的動作
    @Override
    public void onConnected(Bundle connectionHint)
    {
        // 這行指令在 IDE 會出現紅線,不過仍可正常執行,可不予理會
        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (mLastLocation != null)
            {
                mLatitudeText.setText(String.format("%s: %f", mLatitudeLabel, mLastLocation.getLatitude()));
                mLongitudeText.setText(String.format("%s: %f", mLongitudeLabel, mLastLocation.getLongitude()));
            }
        else
            {
                Toast.makeText(this, "偵測不到定位,請確認定位功能已開啟。", Toast.LENGTH_LONG).show();
            }
    }

    @Override
    public void onConnectionFailed(ConnectionResult result)
    {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    }


    @Override
    public void onConnectionSuspended(int cause)
    {
        Log.i(TAG, "Connection suspended");
        mGoogleApiClient.connect();
    }
}

 

這個小程式只能在程式啟動時取得定位,不會隨時更新。


相關筆記 ----
在模擬器取得定位值的操作
Google 地圖 app 速成篇