Android 地圖與定位 (Maps and Positioning)(3)

Positioning Demo

前面兩篇(1, 2)主要都是在講地圖,這篇要來講定位,本篇會用一個簡單的軌跡顯示範例來說明。使用地圖時有些必要條件,雖然第一篇有提過了,不過還是再提一下:
  • 請確定已經下載最新版本的 Google Play services client library 並且加入 Eclipse 中
  • SDK 必須使用 Android version 2.2 (API level 8) 或之後的版本,但因為有用到 MapFragment 所以 Mini target SDK 必須大於 12。(建議直接用最新的SDK會遇到最少問題)

執行步驟

本篇會接在前兩篇所用的範例程式碼之後,這個範例只是在地圖上加上定位的功能而以,步驟如下:
  1. 設定權限
  2. 取得位置提供器
  3. 顯示已知的上一次的位置
  4. 建立偵聽器
  5. 建立"我"的標記
  6. 讓攝影機追著"我"跑
  7. 畫出移動軌跡
  8. 更新位置資訊

1. 設定權限

權限有兩種: 請求的權限會關係到定位的精準度。請求 ACCESS_FINE_LOCATION 意味著也請求了 ACCESS_COARSE_LOCATION。

設定 AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
位置資料的精準度取決於兩個條件,一個是開發者請求的位置提供器權限(例如GPS定位、A-GPS定位),另一個則要看裝置本身的GPS接收器是否夠精準。

2. 取得位置提供器

*備註*:本範例是在第 2 篇的程式內增加有關 GPS 的部份,所以程式碼有些重覆的部份就不貼了,基本上地圖的程式碼都沒有更動。

另外有人有提到測試時畫面一片白,地圖沒有顯示的問題,後來查了一下,網路連線是原因之一,另外一個就是 Api key 的部份,因為範例裡面是用我的 Api key,所以如果你的套件名稱有換過,就會失效,建議你自己申請一個 Api key 來用,不然如果哪天我把 Api key 刪了,原本提供的範例就會不能用。
/** Map */
private GoogleMap mMap;
private TextView txtOutput;
private Marker markerMe;

/** 記錄軌跡 */
private ArrayList<LatLng> traceOfMe;

/** GPS */
private LocationManager locationMgr;
private String provider;

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

@Override
protected void onStart() {
    super.onStart();

    initView();
    initMap();
    if (initLocationProvider()) {
        whereAmI();
    }else{
        txtOutput.setText("請開啟定位!");
    }
}

@Override
protected void onStop() {
    locationMgr.removeUpdates(locationListener);
    super.onStop();
}

private void initView(){
    txtOutput = (TextView) findViewById(R.id.txtOutput);
}

/**
 * GPS初始化,取得可用的位置提供器
 * @return
 */
private boolean initLocationProvider() {
 locationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

 //1.選擇最佳提供器
// Criteria criteria = new Criteria();
// criteria.setAccuracy(Criteria.ACCURACY_FINE);
// criteria.setAltitudeRequired(false);
// criteria.setBearingRequired(false);
// criteria.setCostAllowed(true);
// criteria.setPowerRequirement(Criteria.POWER_LOW);
//  
// provider = locationMgr.getBestProvider(criteria, true);
//  
// if (provider != null) {
//  return true;
// }

 //2.選擇使用GPS提供器
 if (locationMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
  provider = LocationManager.GPS_PROVIDER;
  return true;
 }

 //3.選擇使用網路提供器
// if (locationMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
//  provider = LocationManager.NETWORK_PROVIDER;
//  return true;
// }

 return false;
}
原本寫在 onCreate() 的程式碼現在都移到 onStart() 中。

這裡寫出 3 種常用的位置提供器寫法,第 1 種是開發者提出條件,告訴系統想要取得什麼樣的提供器,Criteria 是用來建立想要取得的提供器的標準,系統會去找出符合或條件之內的提供器。

第 2 種是指名使用 GPS 衛星定位,所以必須判斷裝置是否有 GPS 設備,才能拿來使用。本範例是使用 GPS 定位。

第 3 種是使用網路定位。

一開始我是用第 1 種,結果系統給我網路定位(也就是第 3 種)提供器,我在路上繞了半天都沒什麼反應。後來就使用第 2 種 GPS 定位,只要 5 秒內走超過 5 公尺(依設定條件),就會更新資料,總算有看到地圖在動。

3. 顯示已知的上一次的位置

Location location = locationMgr.getLastKnownLocation(provider);
updateWithNewLocation(location);
如果有上次最後記錄的位置資料,就可以取出來立即顯示。如果沒有的話 location 會是 null。

4. 建立偵聽器

/**
 * 執行"我"在哪裡
 * 1.建立位置改變偵聽器
 * 2.預先顯示上次的已知位置
 */
private void whereAmI(){
 //取得上次已知的位置
 Location location = locationMgr.getLastKnownLocation(provider);
 updateWithNewLocation(location);

 //GPS Listener
 locationMgr.addGpsStatusListener(gpsListener);

 //Location Listener
 int minTime = 5000;//ms
 int minDist = 5;//meter
 locationMgr.requestLocationUpdates(provider, minTime, minDist, locationListener);
 }
一開始先顯示上次的位罝。接著建立 GPS 偵聽器,用來知道 GPS 的狀態。最後建立位置偵聽器,我們設定的更新條件為「更新時間為 5 秒,且距離超過 5 公尺」必須同時符合這兩個條件,偵聽器才會接受更新。

GPS 偵聽器:
GpsStatus.Listener gpsListener = new GpsStatus.Listener() {
    @Override
    public void onGpsStatusChanged(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_STARTED:
            Log.d(TAG, "GPS_EVENT_STARTED");
            Toast.makeText(MainActivity.this, "GPS_EVENT_STARTED", Toast.LENGTH_SHORT).show();
            break;
            case GpsStatus.GPS_EVENT_STOPPED:
            Log.d(TAG, "GPS_EVENT_STOPPED");
            Toast.makeText(MainActivity.this, "GPS_EVENT_STOPPED", Toast.LENGTH_SHORT).show();
            break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
            Log.d(TAG, "GPS_EVENT_FIRST_FIX");
            Toast.makeText(MainActivity.this, "GPS_EVENT_FIRST_FIX", Toast.LENGTH_SHORT).show();
            break;
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
            Log.d(TAG, "GPS_EVENT_SATELLITE_STATUS");
            break;
       }
    }
};
位置偵聽器:
LocationListener locationListener = new LocationListener(){
 @Override
 public void onLocationChanged(Location location) {
  updateWithNewLocation(location);
 }

 @Override
 public void onProviderDisabled(String provider) {
  updateWithNewLocation(null);
 }

 @Override
 public void onProviderEnabled(String provider) {

 }

 @Override
 public void onStatusChanged(String provider, int status, Bundle extras) {
  switch (status) {
      case LocationProvider.OUT_OF_SERVICE:
      Log.v(TAG, "Status Changed: Out of Service");
      Toast.makeText(MainActivity.this, "Status Changed: Out of Service", Toast.LENGTH_SHORT).show();
      break;
      case LocationProvider.TEMPORARILY_UNAVAILABLE:
      Log.v(TAG, "Status Changed: Temporarily Unavailable");
      Toast.makeText(MainActivity.this, "Status Changed: Temporarily Unavailable", Toast.LENGTH_SHORT).show();
      break;
      case LocationProvider.AVAILABLE:
                 Log.v(TAG, "Status Changed: Available");
      Toast.makeText(MainActivity.this, "Status Changed: Available", Toast.LENGTH_SHORT).show();
      break;
  }
 }

};

5. 建立"我"的標記

/**
 * 顯示"我"在哪裡
 * @param lat
 * @param lng
 */
private void showMarkerMe(double lat, double lng){
 if (markerMe != null) {
  markerMe.remove();
 }

 MarkerOptions markerOpt = new MarkerOptions();
 markerOpt.position(new LatLng(lat, lng));
 markerOpt.title("我在這裡");
 markerMe = mMap.addMarker(markerOpt);

 Toast.makeText(this, "lat:" + lat + ",lng:" + lng, Toast.LENGTH_SHORT).show();
}
這裡我原本的想法是單純的去改變 MarkerOptions 的 position 屬性,但是沒反應,應該是 Marker 被建立到地圖上就不能再改變其位置了,所以我後來改為移除前一個 Marker ,同時在新的位置建立一個新的 Marker 。

6. 讓攝影機追著"我"跑

private void cameraFocusOnMe(double lat, double lng){
 CameraPosition camPosition = new CameraPosition.Builder()
    .target(new LatLng(lat, lng))
    .zoom(16)
    .build();

 mMap.animateCamera(CameraUpdateFactory.newCameraPosition(camPosition));
}

7. 畫出移動軌跡

private void trackToMe(double lat, double lng){
 if (traceOfMe == null) {
  traceOfMe = new ArrayList<LatLng>();
}
 traceOfMe.add(new LatLng(lat, lng));

 PolylineOptions polylineOpt = new PolylineOptions();
 for (LatLng latlng : traceOfMe) {
  polylineOpt.add(latlng);
 }

 polylineOpt.color(Color.RED);

 Polyline line = mMap.addPolyline(polylineOpt);
 line.setWidth(10);
}

8. 更新位置資訊

/**
 * 更新並顯示新位置
 * @param location
 */
private void updateWithNewLocation(Location location) {
 String where = "";
 if (location != null) {
  //經度
  double lng = location.getLongitude();
  //緯度
  double lat = location.getLatitude();
  //速度
  float speed = location.getSpeed();
  //時間
  long time = location.getTime();
  String timeString = getTimeString(time);

  where = "經度: " + lng + 
   "\n緯度: " + lat + 
   "\n速度: " + speed + 
   "\n時間: " + timeString +
   "\nProvider: " + provider;

  //"我"
  showMarkerMe(lat, lng);
  cameraFocusOnMe(lat, lng);
  trackToMe(lat, lng);

 }else{
  where = "No location found.";
 }

 //顯示資訊
 txtOutput.setText(where);
}

private String getTimeString(long timeInMilliseconds){
 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 return format.format(timeInMilliseconds);
}
這裡我讓資訊顯示在地圖上方,可以看到經、緯度、速度、時間及提供器。

以下為測試中的截圖,用的是第 1 種提供器的寫法,走來走去都沒什麼反應,圖上顯示的是最後一次的位置的資料,所以表示還是有抓到資料。這個版本效果不太好,簡直不能用。後來成功畫出軌跡的測試沒有圖,有機會在補上。 這篇應該是我寫過最累人的技術文章,大熱天的跑出去曬太陽,定了半天沒反應還要回來重寫,實測加上撰寫,斷斷續續的大概花了三、四天的時間吧,呼~~總算寫完了。看到軌跡出現的那一刻,真是開心 :)
----------------- (沒圖沒真相,補圖 2013/6/11) ------------------ 在運作時,標記會在螢幕中央,可以用手指移動地圖,但只要符合更新條件(5秒5公尺)就會再次把畫面移到正中央,以標記為中心點。這裡因為要截圖,所以把畫面移動過。

範例程式碼已上傳到 GitHub : https://github.com/tony915/GoogleMapDemo

參考資料



本文網址:http://blog.tonycube.com/2013/06/androidmaps-and-positioning3.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

195 則留言

  1. 大大您好,小弟想做Google Maps Android API v2的路徑規劃

    目前使用getLastKnownLocation()取得所在位置以及輸入目的地地址

    加上畫圖的方法,只能畫出兩點之間的直線

    該如何規劃路徑讓它沿著道路走呢?

    爬了很多文,都有點看不懂

    好像需要使用JSON解析出地址經過路徑然後就不太會用了

    大大您解說得很詳細,想請問一下有沒有這方面的經驗呢?

    回覆刪除
    回覆
    1. 後來找到的code,感覺可以用,還在研究中

      http://wptrafficanalyzer.in/blog/driving-route-from-my-location-to-destination-in-google-maps-android-api-v2/

      刪除
    2. 您好,
      我也沒用過,不過這是個好題目,有空來研究看看。

      刪除
  2. 作者已經移除這則留言。

    回覆刪除
  3. 老師我想問你個問題,我已經把位置監聽事件都寫入了,該有的都有了,可是在requestLocationUpdates的時候,他卻抓不到我的LocationListener,還顯示下列錯誤:
    The method requestLocationUpdates(String, long, float, LocationListener) in the type LocationManager is not applicable for the arguments (String, int, int, LocationListener)
    他有提供修正方法,其中一種是改成
    .requestLocationUpdates(provider, minTime, minDist, (android.location.LocationListener) locationListener)
    不過這樣跑程式會崩潰,老師有什麼解決方案嗎? GOOGLE大神找不出來 還是我關鍵字錯了呢?

    回覆刪除
    回覆
    1. 對了,.removeUpdates也會發生一樣的問題,版本是4.2.2,改成4.0.3使用一樣有問題,請老師幫忙看看!感恩!

      刪除
    2. 其實 ...is not applicable for the arguments (String, int, int, LocationListener)這段話就是告訴你,你給的參數內容不符合此方法所要求的參數,所以一定是你給的 4 個參數中發生了錯誤,int 會自動轉 float 及 long ,所以不會有問題。但你說 removeUpdates(..) 也有錯誤,那問題就應該是發生在 LocationListener 所建立的物件上。

      你應該是 import 到 com.google.android.gms.location.LocationListener 這個套件了,
      應該是要 import android.location.LocationListener 這個才對。

      你可以看一下 Reference 中的套件名稱(http://developer.android.com/reference/android/location/LocationManager.html)。

      刪除
    3. 老師,謝謝你!!! 因為老師的一番指點我終於找到問題了 (哭
      我本身是Android初學者,想寫一套關於GPS的程式,但是有很多問題GOOGLE大神都有辦法解決,但是就是很難克服!

      老師的文章清楚易懂,對我的學習是一大福音!我會繼續細讀老師其他文章的!有問題會再請教老師!謝謝老師!

      刪除
  4. 老師您好,我卡在trackToMe()的函數這邊,for (LatLng latlng :traceOfMe )一直無法編譯成功,不知道是為甚麼呢?

    回覆刪除
    回覆
    1. 錯誤訊息應該會告訴你是什麼原因,
      檢查看看有關 traceOfMe 的部份看有沒有漏掉什麼

      刪除
    2. 它顯示traceOfMe Type mismatch: cannot convert from element type Object to LatLng

      刪除
    3. 我查了一下,是我的程式碼貼到部落格時被改掉了
      請把
      ArrayList traceOfMe;
      改成
      ArrayList<LatLng> traceOfMe;


      traceOfMe = new ArrayList();
      改成
      traceOfMe = new ArrayList<LatLng>();

      少了角括號的LatLng,要用HTML code才不會被改掉,你再試看看吧~~謝謝你找到這個問題:)

      刪除
    4. 謝謝老師XD~

      想再請問老師
      private void initView(){
      txtOutput =(TextView)findViewById(R.id.txtOutput);
      }
      裡的R.id.txtOutput出現txtOutput cannot be resolved or is not a field
      請問是不是要自己再layout建立一個叫做txtOutput的TextView呢@~@?

      刪除
    5. 終於~~~~~出現了XD~超感動!!

      在layout加入叫做txtOutput的TextView就出現了!

      謝謝老師>W<~

      刪除
  5. 可以請老師您貼上你import那些東西給我參考參考嗎?

    謝謝老師:))

    回覆刪除
    回覆
    1. import java.text.SimpleDateFormat;
      import java.util.ArrayList;

      import android.app.Activity;
      import android.content.Context;
      import android.graphics.Color;
      import android.location.Criteria;
      import android.location.GpsStatus;
      import android.location.Location;
      import android.location.LocationListener;
      import android.location.LocationManager;
      import android.location.LocationProvider;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.View;
      import android.widget.TextView;
      import android.widget.Toast;

      import com.google.android.gms.maps.CameraUpdateFactory;
      import com.google.android.gms.maps.GoogleMap;
      import com.google.android.gms.maps.MapFragment;
      import com.google.android.gms.maps.model.BitmapDescriptorFactory;
      import com.google.android.gms.maps.model.CameraPosition;
      import com.google.android.gms.maps.model.LatLng;
      import com.google.android.gms.maps.model.Marker;
      import com.google.android.gms.maps.model.MarkerOptions;
      import com.google.android.gms.maps.model.Polyline;
      import com.google.android.gms.maps.model.PolylineOptions;

      刪除
  6. 老師,我想請問一下,為什麼我的程式碼會突然有兩個錯誤在
    1.
    @Override
    protected void onStart() {
    super.onStart();

    initView();
    initMap();
    if (initLocationProvider()) {
    whereAmI();
    }else{
    txtOutput.setText("請開啟定位!");
    }
    }
    中的initMap();這行前面有個X

    2.顯示已知的上一次的位置
    Location location = locationMgr.getLastKnownLocation(provider);
    updateWithNewLocation(location);
    中的updateWithNewLocation(location);前面有個X

    其他部分都沒問題,就在這兩行前面各有一個X,請問我該怎麼解決呢?

    回覆刪除
  7. 應該是漏了 initMap() 方法沒寫吧,請參考第2篇文章,最下面有範例可以下載,裡面有 initMap() 的內容。

    把滑鼠移到錯誤上面,會告訴你發生什麼事

    回覆刪除
    回覆
    1. 第二章是指這個嗎 ?
      http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html

      我找不到ㄟ~~~

      updateWithNewLocation這個方法我也找不到:(

      剛學gps這類 還不是很董~~~

      刪除
    2. 請對該頁面,搜尋文字"範例程式碼下載"。

      刪除
  8. 老師您好

    在updateWithNewLocation裡面的
    txtOutput並沒有辦法顯示出來,
    請問是我xml設定出問題,
    還是被地圖蓋掉了呢?
    可否分享一關於這個textView的一些設定值呢?

    回覆刪除
    回覆
    1. 檢查一下 layout 中元件的順序,從上面看下來,寫在下面的元件會蓋住上面的元件。

      刪除
  9. 老師您好~
    在updateWithNewLocation裡面的

    String timeString = getTimeString(time);
    這行一直無法編譯 是有那裡出錯呢

    回覆刪除
    回覆
    1. 少了方法
      private String getTimeString(long timeInMilliseconds){
      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      return format.format(timeInMilliseconds);
      }

      刪除
  10. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 老師您好 :
      請問該如何實作縮放按鈕?
      我找到縮放的方法是
      googleMap.animateCamera(CameraUpdateFactory.zoomTo(15));
      所以是要獲得當前的ZOOMLEVEL 再去-1嗎?
      感謝 麻煩您了
      也想請問MOVE按鈕的作用 謝謝

      刪除
    2. 第2篇(http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html)文章最後有附範例檔,裡面有寫到zoomTo()

      如果你是要做到每按一次按鈕zoom一次,那是做(+/-)1沒錯,至於要獲得目前的zoom level的想法要倒過,在進入地圖畫面時,你先預設一個level,假設其值為15,之後對這個值做加減,再傳入zoomTo()就可以了。
      ex:
      int level = 15;
      //初始化到15
      zoomTo(level);

      當按下zoom的按鈕時,
      level++; 或 level--;
      zoomTo(level);
      P.S.要增加判斷level上下範圍。

      move 方法是畫面直接移到定點,沒有動畫。

      刪除
    3. 我想請問老師
      Location location = locationMgr.getLastKnownLocation(provider);
      updateWithNewLocation(location);
      我的 location 一直是NULL
      是我手機的GPS出了問題嗎?
      我最近才成功實作出來
      卻一直無法取到自己現在的位置
      您說是取得上一次的位置 那第一次要取位置會有什麼反應呢?
      打擾您了 謝謝

      刪除
    4. 老師您好
      我剛測試之前成功執行的專案
      一樣會取到NULL
      我想問老師 那3個PROVIDER
      如果都打開 就是取消註解
      會挑選哪個使用呢?

      刪除
    5. 看一下API (http://developer.android.com/reference/android/location/LocationManager.html#getLastKnownLocation(java.lang.String))

      這段
      Returns
      the last known location for the provider, or null
      意思是,如果這個 provider 有上次已知的位置就會傳回,否則就回傳 null。

      你說你一直得到 null ,那應該就是沒有上次的已知位置。
      ================================

      如果取消註解,就會依序判斷,先取最佳,沒有就取GPS,再沒有就取網路,如果全都沒有,就回傳 false ,所以如果你去檢查 initLocationProvider() 方法的回傳值就可以知道,provider 到底有沒成功取得。

      另外,我會建議你裝"GPS Status & Toolbox ( https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=zh_TW )
      這個 App,我當初測試時,是先用它來確定 GPS 已經抓到衛星了,再開我的 App 來執行,此外手機可能要拿到戶外或靠窗,不然可能抓不太到衛星。

      刪除
    6. 非常的感謝您如此詳細的解釋
      謝謝

      刪除
  11. 回覆
    1. 第2篇後面有喔,這篇只是在其中增加定位的程式碼,都貼出來囉。

      刪除
  12. 在請問一下 如果要產生kml檔 要如何實做呢??

    回覆刪除
    回覆
    1. 沒做過,查到一些資料,
      這個是教學
      https://developers.google.com/kml/documentation/kml_tut?hl=zh-TW&csw=1
      和這個,KML的格式參考
      https://developers.google.com/kml/documentation/kmlreference

      你可以自己想辦法產生KML檔,或是搜尋已經有的library來用,
      這篇的回答,有提到3個,
      http://stackoverflow.com/questions/2952024/which-java-libraries-can-be-used-to-generate-kml
      可以試試看

      刪除
  13. 請問老師:
    3. 顯示已知的上一次的位置
    Location location = locationMgr.getLastKnownLocation(provider);
    updateWithNewLocation(location);
    此部份是放於onStart()之中嗎?

    上面提到的:"原本寫在 onCreate() 的程式碼現在都移到 onStart() 中",是指說連setContentView(R.layout.activity_main);也要一到此之中嗎?

    回覆刪除
    回覆
    1. 其實不一定要放在哪! 而是你想在什麼時候取得上次的已知位置並且更新,如果要在 App 每次打開時執行,那就是放 onStart() ,如果只有第一次開啟時執行就放在 onCreate(),如果要按下按鈕時才執行,那就是放在按鈕事件中。

      setContentView 我是放在 onCreate() ,有補上程式碼了。

      刪除
  14. 終於成功了,老師的指教真是謝謝!

    回覆刪除
  15. (急)老師為什麼我的:
    import com.google.android.gms.maps.CameraUpdateFactory;
    import com.google.android.gms.maps.GoogleMap;
    import com.google.android.gms.maps.MapFragment;
    import com.google.android.gms.maps.model.BitmapDescriptorFactory;
    import com.google.android.gms.maps.model.CameraPosition;
    import com.google.android.gms.maps.model.LatLng;
    import com.google.android.gms.maps.model.Marker;
    import com.google.android.gms.maps.model.MarkerOptions;
    import com.google.android.gms.maps.model.Polyline;
    import com.google.android.gms.maps.model.PolylineOptions;


    都是錯的><

    回覆刪除
    回覆
    1. 應該是沒有安裝library, 請參考第一篇安裝library

      刪除
    2. 謝謝老師的即時回復
      那這個也是沒有安裝的問題嗎??

      刪除
    3. 作者已經移除這則留言。

      刪除
    4. 作者已經移除這則留言。

      刪除
    5. 在地圖與定位(1)中,步驟一有寫怎麼加library

      刪除
    6. 恩恩 謝謝老師 解決了這個問題^^
      程式沒有錯但是他不能跑 會出現Unfortunately,googleMap has stopped><

      刪除
    7. 老師有沒有E-MAIL之類的可以聯絡你!!!

      刪除
    8. 作者已經移除這則留言。

      刪除
    9. 我沒遇過這個問題,請確認幾件事:
      1.是不是在實機上測試
      2.SDK版本是否符合最低版本
      3.Map 的 API key 是否正確
      4.也許還有其他原因...

      你可以在"就愛寫程式"的FB粉絲頁留訊息給我,
      雖然我很想幫忙,但我也沒那麼多時間可用,
      所以只能盡量回。

      刪除
  16. 作者已經移除這則留言。

    回覆刪除
  17. 老師您好 我想請教一下在activity_main中 需要加入任何的元件嗎?
    目前已有初步簡略地圖可出現了

    回覆刪除
    回覆
    1. 至少要加入 MapFragment 才能顯示地圖,
      其他如範例中的按鈕或文字欄位就看你的需要。

      刪除
  18. 老師你好:
    我有抓你的檔案下來看
    我的layout檔案都會顯示
    Unexpected namespace prefix "xmlns" found for tag fragment
    Unexpected namespace prefix "map" found for tag fragment
    都是MAP的宣告上面的錯誤

    回覆刪除
    回覆
    1. library 的問題,請確認有設定好Google Play Service 及 Android support v4 這兩個 library,
      可以參考前兩篇,
      http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html
      http://blog.tonycube.com/2012/12/androidmaps-and-positioning1.html

      刪除
  19. 老師您好:
    我想請問一下您的按鈕"move"的實作功能
    是把攝影機移到墾丁 可是您的文章最後卻有移動軌跡的路線

    請問要實現有移動軌跡的功能 是只需要在戶外行走即可
    還是也需要先點擊move按鈕才能開啟此功能呢?

    回覆刪除
  20. move 及其他三個功能是前一篇所做的範例,和記錄軌跡這篇無關。

    當你開啟 App 時,只要 Provider 成功取得,就會開始記錄軌跡,但必須符合
    "更新時間為 5 秒,且距離超過 5 公尺"的更新條件,
    才會更新記錄點,也才能畫出一條線。

    實際上在測試的時候,可能要先確定GPS有抓到訊號,建議你裝"GPS Status & Toolbox ( https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=zh_TW )
    這個 App,可以先確定有定到位再試。

    回覆刪除
  21. (急)老師您好
    我近期在學習google map ,GPS & 軌跡部分
    我想請問一下
    我將程式碼及layout貼上想run看看結果為何
    可是它的R檔卻消失了!!?
    R cannot be resolved to a variable<---這錯誤指示
    還有,老師是否可請教一下
    關於v4和v7有甚麼差別嗎?
    之前我做地圖v2用support v4改到v7後就有點怪怪的...

    回覆刪除
    回覆
    1. R 不見,通常是 lib 發生錯誤,無法正確編譯,
      查一下 Project -> Build Path 的設定,
      很有可能是你的 v4,v7所產生的問題。

      如果你是後來才加入v7,
      最好把 v4 的版本換成和 v7同一個的
      (通常在v7目錄的外面就有v4)
      比較不會有問題。

      因為 v7 是相依 v4 的,v7 很多東西都要用到 v4,
      而你匯入 v7 時,通常不會去動到 v4,
      你的 v7 卻可能用到新版的 v4,
      這時就會出錯,R 就會出不來,

      而且如果你同時有用到很多 library ,
      而這些 library 也各自有用到 v4, v7,
      你必須把它們的 v4, v7 全部統一使用一個版本,
      不然會發生衝突,而無法編譯,






      刪除
    2. 摁摁,謝謝老師詳細解說
      讓我長知識了
      萬分感謝~~

      刪除
  22. 老師您好,我直接將您的程式碼import到ADT上,編譯成功,但是傳到手機上執行卻直接打開又退出,手機「很抱歉,GoogleMapDemo已停止」,請問我哪裡錯了呢?
    手機是Nexus5

    回覆刪除
    回覆
    1. 看一下DDMS,會告訴你有什麼錯誤

      刪除
  23. 作者已經移除這則留言。

    回覆刪除
  24. 作者已經移除這則留言。

    回覆刪除
  25. 老師 請問一下我想把開始定位畫線跟結束放到按鈕裡應該要移動哪裡呢?

    回覆刪除
    回覆
    1. 啟動在 onStart() ,結束在 onStop()
      所以可以把

      if (initLocationProvider()) {
      whereAmI();
      }else{
      txtOutput.setText("請開啟定位!");
      }

      移到"開始"按鈕,


      locationMgr.removeUpdates(locationListener);

      移到"結束"按鈕

      刪除
    2. 謝謝您的分享 我趕快去實作看看~

      刪除
  26. 您好,我想請問一下,就是我的程式是用Criteria來定位...手機wifi網路定位有開啟...一切運作正常....但我發現誤差很大...完全定不到我所在的地方...都要過很長一段時間才會定到...所以我想可能是GPS沒開啟的關係...但我開啟手機GPS功能我的app卻無法運作...請問是什麼原因...謝謝你!!!

    回覆刪除
    回覆
    1. 因為您有提到似乎也是這個原因所以您才改用GPS來定位!!請問是不是我的程式也要專門用GPS來做定位...手機開啟GPS才是對的??謝謝你!!

      刪除
    2. 使用Criteria的方式,說明是說會以目前裝置的最佳方式來取得位置提供器,但有可能因為你設定的條件找不到適合的提供器,所以不能用,我當初也是一直無法使用,所以才改成指定使用GPS,但是必須在戶外,不然會不容易抓到衛星。

      用網路的方式會是最快的,但前提是要有網路,而且精準度較差。

      刪除
    3. 您好,我今天繼續使用Criteria的方式修改code...經緯度已經能幾秒鐘就自動更新了...透過地圖幾乎可以定位到我現在(附近)的位置...但偶爾在其他地方不管怎麼測總會跟我所在地差一條街...(EX:正確452巷,測:512巷...雖然前面地址是正確的)...請問這就是網路定位所造成的誤差嗎??還有其實我一直很想知道為什麼手機開啟GPS我的app會無法執行??還是我的程式也要跟您一樣寫GPS定位才可以呢??不好意思問題很長...謝謝你!!

      刪除
    4. 雖然這有點不太可能,但該不會你的手機沒有GPS吧!!
      你可以裝這個App(https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=zh_TW)
      測看看GPS的狀態,確定有抓到後,再開啟你自己的程式。

      誤差的問題,要先確定你是用GPS或網路定位,你說用GPS時,程式無法執行,所以Criteria可能是給你網路定位,網路定位的話誤差一定比較大。

      如果你的定位精準度很要求的話,建議還是要用GPS,或是把 Criteria 的精準度調到最高 ACCURACY_HIGH。

      刪除
    5. 您好....結果是程式有點bug....解決之後已經沒問題了...謝謝你的回答....因為很少有版主會一一回答問題....還有問題希望還能夠繼續請教您^___^

      刪除
  27. 作者已經移除這則留言。

    回覆刪除
  28. 老師
    下載您的程式
    main.xml裡面的fragment標籤裡的Map屬性都出現錯誤
    "Unexpected namespace prefix "xmlns" found for tag fragment"
    請問何解

    回覆刪除
    回覆
    1. library 的問題,請確認有設定好Google Play Service 及 Android support v4 這兩個 library,
      可以參考前兩篇,
      http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html
      http://blog.tonycube.com/2012/12/androidmaps-and-positioning1.html

      刪除
    2. 作者已經移除這則留言。

      刪除
    3. 不好意思老師
      我有設定好了 這兩個都有 但是還是有誤@@
      GOOGLE的是套用的
      V4的是下載後libs資料夾裡面有

      刪除
    4. 1.確認你有把Google Play Service加入專案,
      先把 google-play-services_lib 匯入 Eclipse 成為 library
      然後在你的專案上按右鍵 Properties -> Android -> Library, Add -> google-play-services_lib

      2.檢查一下 xml 檔,
      確定只有一個 xmlns:android="http://schemas.android.com/apk/res/android" 屬性,只有最外層的 layout 可以有,子層的要刪掉

      刪除
    5. 作者已經移除這則留言。

      刪除
    6. 作者已經移除這則留言。

      刪除
    7. 作者已經移除這則留言。

      刪除
    8. 老師我是用V7 他說依賴的V4版本不合
      請問怎麼把這兩個版本弄成一樣的呢

      刪除
    9. 在 v7 的目錄下會有 v4 的 jar ,把它複製到你專案中其他 v4 的目錄,覆蓋那個 v4 的 jar。如果有加入其他 library ,也要蓋掉這個 library 中的 v4 jar,這樣就會有統一的 v4 版本。

      刪除
    10. 老師 Orz 改成一樣的版本後 ADD library v7後就沒跳錯誤
      main的錯誤也消失,不過重開後又出現 到底怎麼辦~clear過重開也一樣
      無視錯誤直接包成APK也會閃退

      刪除
  29. 老師您好
    我發現使用這支程式 android 4.4 之後的版本無法自動取得 或是定時更新位置
    有的時候可以 但是重開機之後就不行 是不是因為舊版本的android上方的按鈕從
    GPS改成位置造成程式無法使用??

    回覆刪除
    回覆
    1. 我查了一下,4.4好像真的會有問題,
      這篇有解法:
      http://stackoverflow.com/questions/19977217/map-fragment-not-working-in-android4-4-kitkat

      在 AndroidManifest.xml 中加入

      刪除
  30. 老師您好 我是用android4.4我直接把你完成的專案下載下來後發現
    xmlns:map="http://schemas.android.com/apk/res-auto"
    會有Unexpected namespace prefix "xmlns" found for tag fragment這個error
    然後下面也有Unexpected namespace prefix "map" found for tag fragment
    這個error
    我有照你之前兩篇得做了google-play-services_lib的設定
    也有supportV4的設定
    可是還是會有錯無法執行
    因為是你得專案  所以應該也不會有其他xml檔案裡面有以下這句xmlns:android="http://schemas.android.com/apk/res/android"
    那請問問題是哪裡呢?
    求解

    回覆刪除
    回覆
    1. Eclipse 選擇 Project -> Clean 專案看看。
      我這邊把 Android 版本換成 4.4 也是OK。

      刪除
    2. 我原先按下clean後沒問題 但是後來<fragment的地方會跑出
      Multiple annotations found at this line:
      - error: No resource identifier found for attribute 'uiZoomControls' in package
      'com.tonycube.googlemapdemo'
      - error: No resource identifier found for attribute 'cameraTargetLat' in package
      'com.tonycube.googlemapdemo'
      等等等很多問題 意思好像是他找不到R裡面的名稱
      而且按下clean後R會不見
      請問一下老師這是甚麼問題

      刪除
    3. 你的 Package 如果不是 "com.tonycube.googlemapdemo"
      記得改成你的。
      另外,我記得 API key 那邊申請也需要 Package 名稱,所以都要改成一樣的。

      另外,因為google-play-service-lib 中有用到v4或v7的jar,要把你的專案中的版本改成和 lib 中的一樣,就是把它複製過來蓋掉你專案的版本,基本上這個錯誤的原因,只是它找錯了library,所以找不到該有的屬性。

      刪除
    4. 那個錯誤我修正好了
      現在會跑出
      fatal exception: main

      刪除
  31. 老師 請問如果要記錄顯示跑了多少公里呢
    該加上什麼程式碼

    回覆刪除
    回覆
    1. traceOfMe 中會儲存所有的移動軌跡,
      你只要有兩個點,
      就能利用距離公式去算出兩點的距離。
      把這些由許多點組成的軌跡,每次取兩個點出來計算
      再將這些值加總,
      就是移動的總長度。

      刪除
    2. 如果要很精確的話,可以參考這篇
      http://stackoverflow.com/questions/14394366/find-distance-between-two-points-on-map-using-google-map-api-v2
      它還計算了因為地球的弧度所形成的距離,而不單單兩點的直線距離。
      不過如果只是在一個小區域裡面移動,我是覺得不太必要計算的那麼精確。

      刪除
    3. 老師,我想問一下如何使用traceOfMe裡面的點來計算距離阿?

      刪除
    4. 利用我前面回文的Stackoverflow文章提到的 Location(http://developer.android.com/intl/zh-tw/reference/android/location/Location.html)
      的 distanceBetween 或 distanceTo 方法來計算兩個點的距離,
      用 for 迴圈去跑 traceOfMe 所收集到的點,一次取兩個就能算距離,
      加總全部點計算後的距離,就是全部軌跡的總長度。

      刪除
  32. 作者已經移除這則留言。

    回覆刪除
  33. 作者已經移除這則留言。

    回覆刪除
  34. 老師,目前用GPS定位可是因為GPS定位她沒有馬上給我值,導致我這一行無法執行 Lat_lo=loc.getLongitude ,有什麼辦法可以讓GPS完整抓到經緯度,才繼續往下做呢?

    回覆刪除
    回覆
    1. 你要先檢查loc是不是null,不是才能getLongitude()
      if (loc != null){
      Lat_lo=loc.getLongitude();
      }

      在 LocationListener() 中可以偵聽位置的改變 onLocationChanged(Loaction loaction),在這裡取位置資訊,通常比較不會有問題。但一樣要檢查 null

      刪除
    2. 老師,我抓出來他完全沒反應,是不是因為我在室內的關係,可是剛剛我有出去室外抓還是抓不到,還有手機裡面有一個位置,然後有一個最近的位置要求,那邊沒有出現我手機所需要的,這部分該怎麼改善呢

      刪除
    3. 你可以裝"GPS Status & Toolbox ( https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=zh_TW )
      這個 App,先確定 GPS 已經定到位了,再開你的 App 來執行。

      此外手機可能要拿到戶外或靠窗,不然可能抓不太到衛星。

      刪除
    4. 摁,有成功抓到了,非常感謝,還有一點就是因為路線會更改,行走過後的路線可以不可抹除掉,我目前是每更新到一次,就整個路徑重新計算,請問老師有沒有更方面的做法呢?

      刪除
  35. 作者已經移除這則留言。

    回覆刪除
  36. 老師你好
    我有按照您的專案做出來了
    我想另外請問
    地圖出來下面的功能裡
    MOVE和AnimTo這兩個是有什麼功用呢
    謝謝

    回覆刪除
    回覆
    1. http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html
      這一篇裡有說明,兩個都是移動地圖到某個點,只是一個有動畫

      刪除
  37. 老師,目前實測的結果他一開始導航也很正常,可是經過一段時間後程式會自動崩潰,想問老師的經驗可能是哪裡出錯了才會造成這樣的狀況?

    回覆刪除
  38. 老師請問如果要直接使用google 做的map,要如何去將他的api開啟傳值過去?

    回覆刪除
  39. 老師您好:
    我最近使用您的程式,我執行時,閃退了,然後跑出以下資訊
    Could not find class 'com.google.android.gms.maps.MapFragment'
    請老師幫忙解答。

    回覆刪除
    回覆
    1. 看一下文章一開始寫的使用地圖的必要條件,確定你都有做。

      刪除
    2. 之前有實做出來,但這陣子開起來要在新增東西時,它就不能執行,跑出這個錯誤訊息

      刪除
  40. 老師我照著您的方法都已經非常成功跑出來了
    我想另外請問
    如果我想要另外設一個按鈕
    然後那個按鈕就可以立刻跳回現在手機的位置
    請問該怎麼做呢

    回覆刪除
    回覆
    1. 1.取得目前手機的位置
      2.移動到此位置(參考這篇http://blog.tonycube.com/2013/01/androidmaps-and-positioning2.html有說怎麼move)

      刪除
    2. 老師您好
      我有看了那篇move的用法
      可是我看是標記在一個指定的地點
      好比墾丁這樣
      move camera
      mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(KENTING, 15));

      那如果是要按下move後
      是會變成一開始進入MAP裡面的位置
      要怎麼做呢

      刪除
    3. move 既然可以移動到指定地點,
      那要移動到"一開始進入MAP裡面的位置",
      只要一開始先把這個點暫存起來,
      之後就可以移到這個點了

      刪除
    4. 老師我很成功的把點暫存起來了
      也可以移動了
      想另外請問
      Snippet這個用法是額外的文字,顯示在標題文字下方
      好比老師在程式裡面
      markerOpt.snippet("於1999年動工,2004年12月31日完工啟用,樓高509.2公尺。");
      的這個部分
      如果我想要把上面這段
      分成三行
      變成
      於1999年動工
      2004年12月31日完工啟用
      樓高509.2公尺

      這樣
      請問老師該怎麼做呢

      刪除
    5. 試試看 \n ,我不確定有沒有用。

      刪除
    6. \n和\r\n都有試過
      都沒有辦法耶

      刪除
    7. 那試HTML的<br>看看,如果不行,那就不知道了。

      刪除
  41. 老師你好
    由於最近專題遇到了一些問題 想請教您
    我們要做旅遊相關的App 要從資料庫裡面抓取旅遊資訊
    然後我們要做按鈕 3KM
    3KM→列出從現在位置3KM以內的旅遊資訊
    想請問設定範圍程式碼要怎麼寫

    //設定距離範圍
    private int DISTANCE = 3000;


    btn.setOnClickListener(new OnClickListener()

    {
    @Override
    public void onClick(View v)
    {
    //按下按鈕後讀取我的位置,定位抓取方式為網路讀取(若欲以GPS為定位抓取方式則更改成LocationManager.GPS_PROVIDER),最後則帶入定位更新Listener。
    mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,10000.0f,LocationChange);

    Intent intent = new Intent();

    intent.setClass(SaleActivity.this,SaleActivity2.class);

    startActivity(intent);

    finish();

    }
    }
    );

    回覆刪除
    回覆
    1. 你的景點A會有一個座標,和你現在位置的座標以[距離公式]做運算,
      小於3000的就是你要的景點。
      把景點A換成用迴圈的方式代入所有的景點,就能判斷哪些是在3000範圍內。

      =====
      考慮到效能,你的資料庫中應該對景點做分類,例如在台北就不應該去判斷台中的點。另外,假如某個點有密集的景點,這些景點最好也歸成同一組,也就是說,只要判斷這一組裡的某一個代表點符合條件,就顯示這一組的所有景點。這樣如果你資料庫裡的景點很多時,才不會浪費時間在做無用的距離判斷。

      刪除
    2. 謝謝老師上面的指導
      但老師有一句:把景點A換成用迴圈的方式代入所有的景點,就能判斷哪些是在3000範圍內
      我不太懂程式碼該如何寫
      希望老師可以指導我一下TT

      刪除
    3. 作者已經移除這則留言。

      刪除
    4. 作者已經移除這則留言。

      刪除
    5. 因為我試了之後 按了3KM和5KM的按鈕之後 他們顯示的東西都一樣
      而且假設按了3KM的按鈕 他資料還是會跑出超出3KM的資料
      我想請問老師是不是程式碼哪裡有出問題><

      刪除
    6. 你的所有的景點應該是放在陣列或集合裡吧,
      那迴圈不就是一個一個景點跑一遍,
      在這個迴圈中把每個景點和目前的座標計算距離,
      就是你要的東西。

      =====
      分段處理:
      1.先只取一個景點A來和目前的位置計算出距離,成功再繼續;
      2.判斷這個距離是否符合你要的,成功再繼續;
      3.上面1,2是處理一個景點,相同程式碼放入迴圈中就可以處理多個景點

      ====
      你前面Post的程式碼,只是開啟一個新的Activity,並把目前的結束,
      完全沒有做任何事。
      如果你的每行程式碼翻譯成中文都不是做你要做的事,那它跑出來的結果也不會是你要的。

      刪除
  42. 作者已經移除這則留言。

    回覆刪除
  43. 老師你好:
    請教你一個問題,我已有經緯度座標。
    是否有辦法透過經緯度座標取得海拔高度呢?
    經緯度座標並不是從location 取得

    回覆刪除
    回覆
    1. 不確定。
      你用經緯度座標去設定 location,然後試看看能不能從它回傳的資料中取得。

      刪除
  44. 作者已經移除這則留言。

    回覆刪除
  45. 老師,我想問一下,為什麼我只能用第3種方法去定位
    其他兩種textview都是顯示 No location found,

    回覆刪除
    回覆
    1. 第1種最佳的定義是由你設定的 criteria 去決定,
      當你把條件設的太嚴苛,它就找不到你所想要的"最佳"Provider。
      第2種要先確定你有開啟GPS。不然可能要再找其他原因。

      刪除
    2. 第2種,GPS 我有在設定>位置>開啟,但是不知道為什麼不能定位
      我在其他人eclipse的教學定位apps可以用到數據或網絡定位
      就是不知道這裡的為什麼不可以

      刪除
    3. 你權限有開嗎?
      API上是說,如果權限不足,會丟出 SecurityException。
      http://developer.android.com/reference/android/location/LocationManager.html#isProviderEnabled%28java.lang.String%29

      不然就要看其他人的教學是怎麼寫的。

      刪除
  46. 老師你好,我也有同樣的問題,我是直接run 你上傳的範例程式碼,但只能用network定位。如果開啟GPS會一直都顯示No location found.. 我只是設定>位置開啟了gps,要怎樣開權限?

    回覆刪除
    回覆
    1. 有跑出"No location found",表示GPS有啟用,只是無法定位。
      你可以裝"GPS Status & Toolbox ( https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=zh_TW ) 這個 App,先確定 GPS 已經定到位了,再開你的 App 來執行。

      你如果是在室內測試,通常是抓不到衛星的,而且不同廠牌的GPS抓衛星的能力也不太一樣。我測的時候是在戶外大晴天的情況,打開GPS也要等一陣子才有資料。

      刪除
  47. 請問這有辦法做道顯示附近的7-11 或全家嗎? 在不設定經緯度的條件下

    回覆刪除
    回覆
    1. 試試看 Google Places API (https://developers.google.com/places/)
      要顯示地點一定要有座標,差別是你自己設定,或是別人(Google)幫你設定。

      刪除
  48. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 您好
      請問要如何在eclipse上,使用自己畫得地圖加上座標?

      刪除
    2. 自己畫的地圖??這我也不知道。

      刪除
  49. 作者您好
    有個問題想要請問有關GPS資料的校正
    因為我在抓GPS的資料的時候
    通常會有10~20公尺的飄移
    常常明明我是在路上走路 可是他會標示我在建築物上
    想問看看作者是怎麼對GPS的資料做校正的
    因為看作者的成果截圖非常漂亮
    謝謝您~

    回覆刪除
    回覆
    1. 我沒有校正,完全就是GPS的數值。
      有可能是因為我騎車,兩個點的距離比較遠,所以比較不會有誤差,你可以測試看看。
      另外,應該也和GPS本身抓的數據有關係吧,如果GPS本身抓取的資料就不準,那要怎麼調。

      刪除
  50. 老師您好
    Q1:請問有什麼方法可以
    把自己做的map app的資料(ex:marker、交通方式)傳到 原廠地圖app上 進行導航?
    簡單來說我就是要把導航和標點的工作弄到原廠地圖app上跑
    (我記得之前做出來地圖右下角可以介接到原廠app,要如何用?)

    回覆刪除
    回覆
    1. 你要查一下原廠app有沒有提供你傳資料給它的方法,否則你是無法做到的。

      刪除
  51. 老師 可以請你貼一下你的layout嗎? 謝謝

    回覆刪除
    回覆
    1. 範例程式碼在 GitHub
      https://github.com/tony915/GoogleMapDemo/blob/master/res/layout/main.xml

      刪除
    2. 老師 假如我從server端 會抓到很多的地址 並要顯示在地圖上
      大概該怎麼寫~~~

      刪除
  52. 可以移动marker的。不用每次删除再新建一个添加。
    可以用Marker.setPosition(LatLng)

    回覆刪除
  53. 赞一下在map上面放TextView的方法。
    原来我在V1下面,用overlay在左上角放置一些信息。可到V2,overlay没有了。(后来又新增了ground overlay和tile overlay,但都和V1的overlay概念不一样)。
    我一直用V1的思路,试图在map上加一个bitmap。但是bitmap随着地图动。于是乎,我又每次计算移动后的位置,来移动bitmap。又麻烦,又愚蠢。

    看到您在图外放了个TextView才豁然开朗,原来有这么简单的方法!
    谢谢。

    回覆刪除
  54. 謝謝板大 可以不用去看英文了...

    回覆刪除
  55. 1.我要怎麼動態新增Maker的點?
    2.為什麼我自己的位置,會一直顯示上一個點?

    謝謝老師

    回覆刪除
  56. 老師您好
    首先先謝謝你的教學檔和demo
    可是不管是我自己的程式碼還是您提供的
    都會出現以下的錯誤訊息

    Could not find class 'android.app.AppOpsManager', referenced from method com.google.android.gms.common.GooglePlayServicesUtil.zzb

    真的很希望可以趕快出現google map啊!!
    請老師幫我找找問題出在哪兒? 謝謝

    回覆刪除
    回覆
    1. 看一下這篇的解答http://stackoverflow.com/questions/31342684/getting-error-could-not-find-class-android-app-appopsmanager-referenced-from
      還有這篇
      http://stackoverflow.com/questions/29033800/noclassdeffounderror-below-sdk-21

      解決方法應該是在Application Class中加入
      @Override
      protected void attachBaseContext(Context base) {
      super.attachBaseContext(base);
      MultiDex.install(this);
      }

      試試看吧~~

      刪除
  57. 老師您好
    請問如何InfoWindowAdapter上面加入button的Listener呢?
    目前在InfoWindowAdapter上有一個textview 和 button,但是使用Button.OnClickListener無法監聽到事件
    請老師幫忙。
    謝謝!

    回覆刪除
    回覆
    1. 官方文件上的說明(https://developers.google.com/maps/documentation/android-api/infowindows)藍底Note的部份,InfoWindow 其實最終會被描繪成圖片,所以基本上你無法補捉到上面button的事件,所有的view最終都會被畫成圖,所以只能補捉整個InfoWindow 的click事件,因此應該是沒辦法做到。

      不過,好像有人用其他方法做出來,在這裡
      http://stackoverflow.com/questions/14123243/google-maps-android-api-v2-interactive-infowindow-like-in-original-android-go/15040761#15040761
      有點複雜,你自己研究看看吧~~

      刪除
  58. 老師您好 我的這行 mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();好像會抓不到值QQ 他給的錯誤訊息是Caused by: java.lang.NullPointerException 請問有甚麼解決方式嗎??

    回覆刪除
    回覆
    1. 出現 NPE 的有可能是 getFragmentManager(),你可以試試看改成這樣

      mMap = (MapFragment) getFragmentManager().findFragmentById(R.id.map)

      然後把這行之後的所有程式碼先註解掉,然後檢查 mMap 是不是一樣是 null,
      如果是那問題就是它,至於原因,有可能是 API 版本不一致或你有用到 support library,
      所以可能呼叫不一致的 library,可以看 import 是呼叫哪個,總之應該是出在版本對不上。

      刪除
  59. 老師,可以請問一下嗎?如何在地圖上動態加入新的使用者位置(在地圖上顯示兩個以上的使用者位置)?3Q~

    回覆刪除
    回覆
    1. 要加入其他人的位置,表示你要有其他人的座標資訊,
      這部份你可以將所有人的座標資料上傳伺服器,
      然後每個人在從伺服器下載別人的座標資訊並顯示出來。

      刪除
  60. 老師你好:
    請教一下,
    我在新專案右鍵->Properties->Android->Add->google-play-service-lib 為何會出現R.layout錯誤
    setContentView(R.layout.activity_main);
    錯誤訊息(R cannot be resolved to a variable)
    請問該如何解決?

    回覆刪除
    回覆
    1. 可能是google-play-service-lib版本不對或衝突,導致無法編譯成R檔,
      也有可能和 support library 版本不合而發生。

      可以把SDK的 google-play-service-lib 和 support library 全都升到最新版本,然後去 SDK 目錄下加入這些新版本。

      以上是 Eclipse 的做法,如果你用 Android Studio 只要去設定 gradle 檔即可,不用自己手動加 library。

      另外,build-tool 也記得要更新。

      刪除
  61. 老師你好:
    請教一下
    我的:private void whereAmI() {
    Location location = locationManager1.getLastKnownLocation(provider);
    updateWithNewLocation(location);
    locationManager1.addGpsStatusListener(gpsListener);
    long mintime = 5000;
    float minDist = 5.0f;
    Criteria criteria = new Criteria();
    locationManager1.requestLocationUpdates(criteria, mintime, minDist, provider ,);
    }
    都出現紅字不知那裡有錯
    請問該如何解決呢??

    回覆刪除
    回覆
    1. 看一下錯誤訊息,它會告訴你是什麼原因

      刪除
  62. 老師你好,
    我想問如何使用一個button執行/取消 track to me?
    (即,當我按下button,就會開始記錄路線,再按一下就停止,並計算總距離。)
    而且如何取得第2個獲取的location(lat,long)?

    My coding:

    private void trackToMe(double latitude, double longitude){

    if (traceOfMe == null) {
    traceOfMe = new ArrayList();
    }
    traceOfMe.add(new LatLng(latitude, longitude));


    //------track to me
    final Button btn_track = (Button) findViewById(R.id.btn_track);
    final String btnTrackText = btn_track.getText().toString();
    btn_track.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    PolylineOptions polylineOpt = new PolylineOptions();
    if(btnTrackText.equals("Start")){
    btn_track.setText("Stop");

    for (LatLng latlng : traceOfMe) {
    polylineOpt.add(latlng);
    polylineOpt.color(Color.RED);

    Polyline line = mMap.addPolyline(polylineOpt);
    line.setWidth(10);
    }
    } else {
    btn_track.setText("Start");

    }
    }
    });

    回覆刪除
    回覆
    1. btn_track.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      String buttonType = ((Button)v).getText().toString();
      if (buttonType.equals("Start")){
      ((Button)v).setText("Stop");
      //在這裡啟動
      whereAmI();
      }else{
      ((Button)v).setText("Start");
      //在這裡停止並計算
      //把 location 偵聽器 remove 掉
      //檢查 traceOfMe 裡面有沒有座標資料,
      //有的話一次取兩個點算距離,然後加總
      }
      }
      }

      刪除
  63. 老師
    我4.4的版本
    自己參考您這篇寫了類似的東西
    我的問題是
    我用電腦模擬器跑得出googlemap
    用自己手機卻跑不出來
    他會灰灰的
    但我手機確實有收到定位,以及我的trceofme陣列也有確實接收到每個點
    唯一就是跑不出googlemap
    我的fragment跑不出來卻跑的出來textview跟button
    但fragment左下角有google圖式
    可是地圖就是灰灰的

    回覆刪除
    回覆
    1. 你的程式應該沒問題,
      有可能是在 Api key 的部份,
      確定你有申請成功及設定正確,
      另外,如果你是用 debug 的金鑰去申請 Api Key,
      在 release 的時候,要再用這個金鑰去申請,
      可以參考前一篇(http://blog.tonycube.com/2012/12/androidmaps-and-positioning1.html)
      另外,之前我申請後要等一段時間才能使用,可能要半天到一天,
      但現在我不知道是不是還要那麼久。

      刪除
    2. 謝謝老師
      果然是API的問題
      我當初只有用DEBUG的
      但我手機都是裝發行版本
      因為我手機之前USB燒壞無法直接測試
      謝謝你

      刪除
  64. 你好,我想請問一下,因為我想要顯示兩個marker,但是我遇到一個問題就是一個maker需要用到onInfoWindowClick,另一個就不需要用到onInfoWindowClick,我不知道老師知道怎樣去處理嗎?

    回覆刪除
    回覆
    1. 查一下文件
      https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.OnInfoWindowClickListener
      ====
      public abstract void onInfoWindowClick (Marker marker)
      Called when the marker's info window is clicked.
      ====
      看說明應該是你只要寫一個 onInfoWindowClick,丟不同的 Marker 進去,就可以針對這個 Marker 做回應。不是很確定,你要試一下。

      刪除
  65. 作者 我問一下
    我複製您的CODE 但是一直有個ERROR

    protected void onStop() {

    locationMgr.removeUpdates(locationListener);
    super.onStop();
    }


    locationMgr.removeUpdates(locationListener); 這段是紅字

    他寫原因是

    Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`


    只要有locationMgr的那段都會錯

    請問該怎麼解決...

    回覆刪除
    回覆
    1. 應該是6.0之後新的授權機制的問題,因為權限有可能被使用者取消,所以要先確認,你可以參考這篇去修改
      http://blog.tonycube.com/2016/07/android-60-android-save-image-and.html

      刪除
    2. 作者不好意思
      所以如果是用這個地圖定位code
      那我要怎麼改 QAQ

      刪除
    3. 你可把授權那部份的程式碼(在這條線之下////////////////////////////////////////////////
      )複製到你的專案,稍微修改一下把不要的刪掉,

      然後在取得 LocationManager 之前先檢查授權,或要求權限,
      if (!hasPermission()) {...略}
      所以可能要放在 initLocationProvider() 最開頭,
      還有其他會呼叫 LocationManager 的地方

      最後,記得把要求的授權改成你要的,像是 ACCESS_COARSE_LOCATION

      應該是這樣吧,你要自己試一下

      刪除
  66. 你的按鈕應該只能用來確認方向,例如,
    有一個變數為 isGoing 用來儲存布林值,true 表示前往,false 表示返回,
    而按鈕的功能只是用來改變這個值。

    接著,在 trackToMe() 方法中,
    假設你先建了兩個用來儲存軌跡的串列 traceGoing, traceComeback
    你就可以用 isGoing 來判斷現在的座標值是要儲存在哪個串列中,
    大概會像這樣:
    Boolean isGoing = true;//預設為前往
    btnGoing.setOnClickL…略 {
    isGoing = true;
    }
    btnComeback.setOnClickL…略 {
    isGoing = false;
    }

    trackToMe(){
    if (isGoing){
    //將軌跡加入 traceGoing
    traceGoing.add(new LatLng(lat, lng));
    }else{
    //將軌跡加入 traceComeback
    traceComeback.add(new LatLng(lat, lng));
    }

    //這時候判斷一下這兩個 trace 串列中有沒有值,
    //有的話就各別依指定的顏色畫出軌跡
    }

    你在試試看吧~

    回覆刪除
    回覆
    1. Tony老師,看完你的回覆後我已經寫出來,真的很謝謝你,接下來我繼續加入新功能,目前要往TCPIP的方向去,將目前的GPS位置傳給Server,再從另一個客戶端顯示GPS位置,如果有問題會再來請教你,非常感謝

      刪除
  67. 想請問您的這種寫法與事先implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    OnMapReadyCallback {....
    這種寫法差在哪呀,因為看到一些書籍上面寫說要持續更新得到目前的位置得用這種寫法,與您的不同不太懂差異在哪

    回覆刪除
    回覆
    1. 我這篇文章有點舊了,那時候還沒有 Google Places API 可以用(或是我不知道),
      是直接呼叫系統內建的服務來取得位置資訊。
      用 API 比較方便,很多條件不用自己去判斷,等 Callback 回來就會自己執行,
      比較不容易出錯,建議用 GoogleApiClient 提供的工具。

      刪除
    2. 作者已經移除這則留言。

      刪除
    3. 哦哦了解了,那我再去了解一下API的部分好了,另外我想請問有辦法把利用您程式所記錄下來的軌跡變成GPX檔輸出至手機的儲存空間嗎,因為上網都找不太到資料,可以請您大略講一下實作方法,或是跟我講有什麼關鍵字讓我收尋google嗎,謝謝。

      刪除
    4. GPX 只是一個 GPS 交換格式,可以自行輸出,請參考 Wiki 上的格式
      https://zh.wikipedia.org/wiki/GPX

      程式碼中有用 traceOfMe 這個變數來儲存經緯度,所以你可以直接將它轉換成 GPX 中的

      <trkseg>
      <trkpt lat="47.644548" lon="-122.326897"> <=== 表示一個點座標
      <trkpt lat="47.644548" lon="-122.326897">
      ...更多點座標
      </trkseg>

      然後存成檔案就可以了,記得找 XML 函式庫工具來建立。

      刪除
    5. 補正:Google Places API 和 GoogleApiClient 是不同的東西,我有點搞錯了

      刪除
  68. 老師請問一下這篇如果現在用android studio 的 Google Places API有辦法照著做嘛!? 因為我是新手目前了解得不多 想請教一下謝謝!

    回覆刪除
    回覆
    1. 我查了一下,你看這裡
      https://developers.google.com/maps/android/?hl=zh-tw
      //
      Google Map API 是針對地圖應用,比較偏向「定位」這件事;
      Google Place API 是針對「地點」的應用,
      它提供了許多地點的資料,比較適合用在找「地點」這件事情上
      //
      所以應該還是要用 Google Map API
      https://developers.google.com/maps/documentation/android-api/start?hl=zh-tw
      其實現在都有中文手冊,說明還滿詳細的

      刪除
  69. 不好意思請問一下,如果路徑需要隨時間改變顏色,polyline要如何更改呢?

    回覆刪除
    回覆
    1. 這段是指定軌跡的顏色
      polylineOpt.color(Color.RED);
      就看你要在什麼時間指定它不同的顏色

      刪除
    2. 斐常感謝<(_ _)> 不好意思再詢問一個問題
      如果我用button清除掉走過的路徑 我應該如何使用?

      刪除
    3. 軌跡都存在這個變數
      private ArrayList traceOfMe;
      當按下按鈕時把它清空就可以了
      如果沒有立即清除,那就要立即呼叫 updateWithNewLocation(location)
      並給它一個目前的座標值

      刪除

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。