Androidで位置情報 その2
続きです。
現在位置の取得
MainActivity.java
import android.app.Activity; import android.os.Bundle; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; public class MainActivity extends Activity implements LocationListener { private LocationManager _lcmLocationManager; private String _strBestProvider = ""; private double _dblCurrentLatitude = 0; private double _dblCurrentLongitude = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 位置情報をコントロールするLocationManagerを取得. _lcmLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria crtCriteria = new Criteria(); // 位置情報の精度を指定する. crtCriteria.setAccuracy(Criteria.ACCURACY_FINE); // 消費電力を指定する. crtCriteria.setPowerRequirement(Criteria.POWER_MEDIUM); // 方位情報を取得可能にする. crtCriteria.setBearingRequired(true); // 方位情報の精度を指定する. crtCriteria.setBearingAccuracy(Criteria.ACCURACY_FINE); // 速度情報を取得可能にする. crtCriteria.setSpeedRequired(true); // 速度情報の精度を指定する. crtCriteria.setSpeedAccuracy(Criteria.ACCURACY_FINE); // 高度情報を取得可能にする. crtCriteria.setAltitudeRequired(true); // 高度情報の精度を指定する. crtCriteria.setVerticalAccuracy(Criteria.ACCURACY_FINE); // ロケーションプロバイダの取得 _strBestProvider = _lcmLocationManager.getBestProvider(crtCriteria, true); // LocationListenerを登録 _lcmLocationManager.requestLocationUpdates(_strBestProvider, 0, 0, this); } @Override public void onProviderEnabled(String str) { // 今回は何もしない. } @Override public void onProviderDisabled(String str) { // 今回は何もしない. } @Override public void onLocationChanged(Location lctNewLocation) { // 取得した緯度、経度をセットする. _dblCurrentLatitude = lctNewLocation.getLatitude(); _dblCurrentLongitude = lctNewLocation.getLongitude(); // 後で処理. // 方位情報 // lctNewLocation.getBearing(); // 速度情報. // lctNewLocation.getSpeed(); // 高度情報. // lctNewLocation.getAltitude(); } @Override public void onStatusChanged(String str, int in, Bundle bnd) { // 今回は何もしない. } }
とりあえず取得可能な情報をかたっぱしから取ってみた、という感じです。
onProviderDisabledでセットした緯度、経度を使って、タイマーでマーカーを設置していく、と。
ここで問題が発生したのですが、前回のコードで、マーカーを追加するメソッド(addMarker)をタイマーから呼び出すと、GoogleMapのオブジェクト(_ggmMap)がnullになってしまうのでした。
そのあたりの対策と、Switchボタンを追加して、Onにした時にタイマーの開始・位置情報の取得を始める処理を加えたのがこちらです。
package jp.co.masanori.roadrawer; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.CompoundButton; import android.support.v4.app.FragmentActivity; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends FragmentActivity implements LocationListener { private LocationManager _lcmLocationManager; private GoogleMap _ggmMap; private String _strBestProvider = ""; private double _dblCurrentLatitude = 0; private double _dblCurrentLongitude = 0; private MainActivity _actMain; private Timer _tmrAddMarker; // タイマーで実行するクラス. private CtrlTimer _cttCtrlTimer; private CompoundButton _cmbSwtStart; private boolean _isTimerStarted = false; private final int MESSAGE_ADD_MARKER = 0; private Handler _hndBleHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_ADD_MARKER: _actMain.addMarker(_actMain); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); _actMain = new MainActivity(); // 位置情報をコントロールするLocationManagerを取得. _lcmLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria crtCriteria = new Criteria(); // 位置情報の精度を指定する. crtCriteria.setAccuracy(Criteria.ACCURACY_FINE); // 消費電力を指定する. crtCriteria.setPowerRequirement(Criteria.POWER_MEDIUM); // 方位情報を取得可能にする. crtCriteria.setBearingRequired(true); // 方位情報の精度を指定する. crtCriteria.setBearingAccuracy(Criteria.ACCURACY_FINE); // 速度情報を取得可能にする. crtCriteria.setSpeedRequired(true); // 速度情報の精度を指定する. crtCriteria.setSpeedAccuracy(Criteria.ACCURACY_FINE); // 高度情報を取得可能にする. crtCriteria.setAltitudeRequired(true); // 高度情報の精度を指定する. crtCriteria.setVerticalAccuracy(Criteria.ACCURACY_FINE); // ロケーションプロバイダの取得 _strBestProvider = _lcmLocationManager.getBestProvider(crtCriteria, true); // 記録開始用Switchの設定. _cmbSwtStart = (CompoundButton)findViewById(R.id.swtStart); _cmbSwtStart.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // 状態が変更されたら実行. if(isChecked) { if((_strBestProvider != null) &&(_lcmLocationManager.isProviderEnabled(_strBestProvider))) { // LocationListenerを登録 _lcmLocationManager.requestLocationUpdates(_strBestProvider, 0, 0, _actMain); // タイマーの設定.ストップすると破棄されるため、毎回生成. _cttCtrlTimer = new CtrlTimer(); _tmrAddMarker = new Timer(); // 1分ごとにマーカーを置く. _tmrAddMarker.schedule(_cttCtrlTimer, 10000, 60000); _isTimerStarted = true; } else { _cmbSwtStart.setChecked(false); } } else { if(_isTimerStarted) { // 位置情報の更新ストップ. _lcmLocationManager.removeUpdates(_actMain); // タイマーをストップ. _tmrAddMarker.cancel(); _tmrAddMarker.purge(); _cttCtrlTimer.cancel(); } } } }); // マップの表示. this.showMap(); } private void showMap() { if (_actMain._ggmMap == null) { // マップの表示. _actMain._ggmMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap(); } } public void addMarker(MainActivity actMain) { if (actMain._ggmMap != null) { // 現在地にマーカーを追加する. actMain._ggmMap.addMarker(new MarkerOptions().position( new LatLng(_dblCurrentLatitude, _dblCurrentLongitude)).title( "Lat:" + _dblCurrentLatitude + " Lon:" + _dblCurrentLongitude)); } } @Override public void onProviderEnabled(String str) { // 今回は何もしない. } @Override public void onProviderDisabled(String str) { // 今回は何もしない. } @Override public void onLocationChanged(Location lctNewLocation) { // 取得した緯度、経度を保持する. _dblCurrentLatitude = lctNewLocation.getLatitude(); _dblCurrentLongitude = lctNewLocation.getLongitude(); // 後で処理. // 方位情報 // lctNewLocation.getBearing(); // 速度情報. // lctNewLocation.getSpeed(); // 高度情報. // lctNewLocation.getAltitude(); } @Override public void onStatusChanged(String str, int in, Bundle bnd) { // 今回は何もしない. } public class CtrlTimer extends TimerTask { @Override public void run() { // マーカーの追加. _hndBleHandler.sendEmptyMessage(MESSAGE_ADD_MARKER); } } }
実際に位置情報を取ってみました(プロジェクト名が異なっていますが、気にしないでください...)
【参考】
- Location and Maps - Android Developers
- Android 位置情報を取得するには / Getting Started - Tech Booster
- 【Android】位置情報の取得方法 - AdMax Tech Blog
今後の課題点
- GPSをOnにして位置情報を取得開始後、しばらく移動せずに待っておかないと位置情報が取れず、(0, 0)の位置にマーカーが置かれてしまう
- 位置情報の取得を開始して、そのままバックグラウンドにするとフォアグラウンドにした後も位置情報が取れない
- GPSを切った状態でSwitchをOnにしても、位置情報の取得は開始されないが、Switchの表示が元に戻らない
- GPSやネットワークが接続されていない時のアラートの表示
- マーカーを置いた時に、マーカーを置いた緯度・経度が地図の中央に来るようにしたい
- 現在ネットワークに接続して、地図を表示していないと(GoogleMapオブジェクトがnullのため)マーカーが置けない。DBにデータを保存して、ネットワークに接続した時にマーカーを表示するようにしたい。
- マーカーを置いた場所同士を線でつなぎたい
- 日付などの条件で検索して、マーカーや線を表示したい
まだまだ課題は大量に残っていますが、引き続きマイペースにやってみます。