marunomaruno-memo

marunomaruno-memo

[Android] 位置情報(2) トグルボタンによる位置情報の取得の開始・終了

2012年01月09日 | Android
位置情報(2) トグルボタンによる位置情報の取得の開始・終了
================================================================================

■ 位置情報の取得の開始・終了をトグルボタンで制御する

位置情報の取得は、「開始/終了」ボタンのクリックによる。このボタンはトグルボタン
になっていて、そのときの状態を保存する必要がある。

また、位置情報は、コンソールに表示するように、画面にスクロールして表示される。

トグルボタンは、実行状態をインスタンス変数で保持する。ただし、画面が切り替わった
ときなどは、Activity のインスタンスは新しく生成されるため、Activity クラスの
    onSaveInstanceState()
    onRestoreInstanceState()
メソッドをオーバーライドして、保存して、復帰する必要がある。

コンソール表示のイメージは、つぎの既出のドキュメントを参照されたい。
    標準出力 - あて先に画面を追加する
    http://blog.goo.ne.jp/marunomarunogoo/d/20111222

コード
---
Gps02Activity.java        アクティビティ
GpsLocationListener.java  ロケーションのリスナー(Gps01 と同じ)
TextViewPrintStream.java  System.out オブジェクトの置き換え(ystemOut01 と同じ)
---

Gps01 と同じ、というのは、以下のドキュメントの Gps01 プロジェクトのソースと同じ
ソースである。
    位置情報 (1) 無線ネットワークを使った位置情報
    http://blog.goo.ne.jp/marunomarunogoo/d/20111218


◆ アクティビティのクラス

□ Gps02Activity.java
---
package jp.marunomaruno.android.sample;

import android.app.Activity;
import android.location.Criteria;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

/**
 * 位置情報を表示するアクティビティ。
 * トグルボタンで、開始・終了する。
 * @author marunomaruno
 * @version 1.0, 2011-12-14
 * @since 1.0
 */
public class Gps02Activity extends Activity {

    private boolean isRunning = false;    // 現在起動中かどうか        // (1)
    private static final String IS_RUNNING = "isRunning";      // (2)

    private LocationManager locationManager;
    private LocationListener sensorEventListener; 
                    // onPause()が呼ばれたときに、すべてのリスナーを解除するため
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(getString(R.string.logTag), String.format("onCreate() "
                + getString(R.string.sensor_value_format_for_timestamp),
                getString(R.string.time), System.currentTimeMillis()));

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 標準出力を置き換え(標準出力(LogCat)+テキストビュー)
        TextView sysout = (TextView) findViewById(R.id.sysout);    // (3)
        System.setOut(new TextViewPrintStream(System.out, sysout));    // (4)
        System.setErr(new TextViewPrintStream(System.err, sysout));    // (5)

        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
    }

    @Override
    protected void onResume() {
        Log.d(getString(R.string.logTag), String.format("onResume() "
                + getString(R.string.sensor_value_format_for_timestamp),
                getString(R.string.time), System.currentTimeMillis()));

        super.onResume();

        Button startStopButton = (Button) findViewById(R.id.startStopButton);

        Log.d(getString(R.string.logTag), "起動中 = " + isRunning);
        
        if (isRunning) {    // 起動中のとき、終了ボタンを表示する      // (6)
            startStopButton.setText(R.string.stopButton);    // (7)

        } else {
            startStopButton.setText(R.string.startButton);    // (8)
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {    // (9)
        Log.d(getString(R.string.logTag), String.format("onRestoreInstanceState() "
                + getString(R.string.sensor_value_format_for_timestamp),
                getString(R.string.time), System.currentTimeMillis()));

        super.onRestoreInstanceState(savedInstanceState);
        isRunning = savedInstanceState.getBoolean(IS_RUNNING);        // (10)
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {            // (11)
        Log.d(getString(R.string.logTag), String.format("onSaveInstanceState() "
                + getString(R.string.sensor_value_format_for_timestamp),
                getString(R.string.time), System.currentTimeMillis()));

        super.onSaveInstanceState(outState);
        outState.putBoolean(IS_RUNNING, isRunning);                    // (12)
    }

    /**
     * 開始/終了ボタン押下時
     * @param view
     */
    public void onClickStartStopButton(View view) {                    // (13)
        Log.d(getString(R.string.logTag), String.format("onClickStartButton() "
                + getString(R.string.sensor_value_format_for_timestamp),
                getString(R.string.time), System.currentTimeMillis()));

        Button startStopButton = (Button) view;
        
        if (isRunning) {    // 起動中のとき、とめる    // (14)
            // すべてのリスナーを解除する
            removeListeners();

            // 開始ボタンにする
            startStopButton.setText(R.string.startButton);
            System.out.printf(getString(R.string.stopMessageFormat), 
                    System.currentTimeMillis());
            System.out.println();

        } else {
            // 起動していないとき、
            // GPSロケーションを取得して登録する
            setProviderAndListener();
            
            // 停止ボタンにする
            startStopButton.setText(R.string.stopButton);
            System.out.printf(getString(R.string.startMessageFormat), 
                    System.currentTimeMillis());
            System.out.println();
        }
        isRunning = !isRunning;    // 起動中状態を逆にする            // (15)
        Log.d(getString(R.string.logTag), String.format(
                "起動中 = %b, 開始時刻  = %tT", isRunning, System
                        .currentTimeMillis()));
    }

    private void setProviderAndListener() {
        // プロバイダーを取得する条件を作成する
        Criteria fineAndLowPower = new Criteria();
        fineAndLowPower.setAccuracy(Criteria.ACCURACY_FINE);
        fineAndLowPower.setPowerRequirement(Criteria.POWER_LOW);
        
        // プロバイダーを取得する
        String provider = locationManager.getBestProvider(fineAndLowPower, true);
        Log.d(getString(R.string.logTag), "provider = "
                + provider);
        TextView providerNameTextView = (TextView) findViewById(R.id.provider_name);
        providerNameTextView.setText(provider);
        
        // プロバイダーがみつからなければ、NETWORK_PROVIDER を設定しておく
        if (provider == null) {
            provider = LocationManager.NETWORK_PROVIDER;
            providerNameTextView.setText(R.string.no_provider);
        }
        
        // 位置情報のリスナーを取得して登録する
        sensorEventListener = GpsLocationListener.getInstance(this);
        locationManager.requestLocationUpdates(
                provider, 
                0, 
                0, 
                sensorEventListener);
    }

    private void removeListeners() {
        if (sensorEventListener != null) {
            locationManager.removeUpdates(sensorEventListener);
        }
    }
}
---

(1)(2) 現在起動中かどうかをあらわすインスタンス変数

    private boolean isRunning = false;    // 現在起動中かどうか        // (1)

トグルボタンを設定するために、現在の状態(起動中/停止中)をあらわすフラグをインス
タンス変数として保持する。
これは、(9)の onSaveInstanceState() メソッドで保存して、 (8)の 
onRestoreInstanceState() メソッドで復帰することで、画面が隠れてまた現れたときに、
別のアクティビティのインスタンスが生成されても、データが保持されるようにしておく
ためである。
この、保存するときのキーは、文字列なので、つぎのような定数を用意しておく。

    private static final String IS_RUNNING = "isRunning";      // (2)


(3)-(5) 標準出力を置き換え(標準出力(LogCat)+テキストビュー)

標準出力と標準エラー出力を置き換える。

    TextView sysout = (TextView) findViewById(R.id.sysout);    // (3)
    System.setOut(new TextViewPrintStream(System.out, sysout));    // (4)
    System.setErr(new TextViewPrintStream(System.err, sysout));    // (5)

コンソール表示のイメージは、つぎの既出のドキュメントを参照されたい。
    標準出力 - あて先に画面を追加する
    http://blog.goo.ne.jp/marunomarunogoo/d/20111222


(6)-(8) 起動中と停止中で、トグルボタンの表示を切り替える

    if (isRunning) {    // 起動中のとき、終了ボタンを表示する      // (6)
        startStopButton.setText(R.string.stopButton);    // (7)

    } else {
        startStopButton.setText(R.string.startButton);    // (8)
    }


(9) onRestoreInstanceState() メソッドのオーバーライド

onSaveInstanceState() メソッドで保存されたインスタンスの状態を、アクティビティが
再初期化される直前に呼び出される

    protected void onRestoreInstanceState(Bundle savedInstanceState) {    // (9)


(10) 実行状態を復帰する

isRunning は boolean 型なので、Bundle クラスの getBoolean() メソッドを使う。

    isRunning = savedInstanceState.getBoolean(IS_RUNNING);        // (10)

保存されている型によって、さまざまな取得メソッドがある。

・基本的な型を取得するメソッド
---
xxx    getXxx(String key)
xxx    getXxx(String key, xxx defaultValue)
xxx[]  getXxxArray(String key)
---
    xxx: boolean, byte, char, double, float, int, long, short, CharSequence, 
         String, Parcelable

なお、CharSequence, int, String は、上記に加えてつぎのArrayListを保存できるメソ
ッドがある。
---
ArrayList<xxx>  getXxxArrayList(String key)
---
Parcelable 型については、ジェネリクスとして、型が考えられている。詳しくは、API 
を参照。

上記以外に、つぎのメソッドがある。
---
Object          get(String key)
Bundle          getBundle(String key)
Serializable    getSerializable(String key)
<T extends Parcelable> SparseArray<T>     
                getSparseParcelableArray(String key)
---


(11) onSaveInstanceState() メソッドのオーバーライド

インスタンスの状態をアクティビティに保存するときに呼び出される。

    protected void onSaveInstanceState(Bundle outState) {            // (11)


(12) 実行状態を保存する

        outState.putBoolean(IS_RUNNING, isRunning);                    // (12)

取り出すためのキー(文字列)と、保存したい値を指定して、保存する。
保存したものは、対応する getXxx() メソッドによって取得する。

・基本的な型を保存するメソッド
---
void  putXxx(String key, xxx value)
void  putXxxArray(String key, xxx[] value)
---
    xxx: boolean, byte, char, int, double, float, long, short, CharSequence, 
         String, Parcelable

なお、CharSequence, int, String は、上記に加えてつぎのArrayListを保存できるメソ
ッドがある。
---
    void  putXxxArrayList(String key, ArrayList<xxx> value)
---
Parcelable 型については、ジェネリクスとして、型が考えられている。詳しくは、API 
を参照。

上記以外に、つぎのメソッドがある。
---
void  putBundle(String key, Bundle value)
void  putSerializable(String key, Serializable value)
void  putSparseParcelableArray(String key, SparseArray<? extends Parcelable> value)
---

ただ、不思議なのは、Object 型を取得するための
    Object     get(String key)
メソッドがあるのに、これに対する
    void     put(String key, Object value)
がない。なぜ?
一般的なオブジェクトを保存したい場合は、シリアライズして保存することになるのか?
または、いちいち Parcelable インターフェースを実装したクラスを作るのか?
Serializable の実装の方が簡単かな。


(13) 開始/終了ボタン押下時
    public void onClickStartStopButton(View view) {                    // (13)


(14) 実行状態によって、動きを変える

    if (isRunning) {    // 起動中のとき、とめる    // (14)


(15) 起動中状態を逆にする

    isRunning = !isRunning;    // 起動中状態を逆にする            // (15)

トグルボタンで、boolean 型なので、否定すればよい。


◆ 既出のソースの再掲

つぎのソースは、以下のドキュメントと同じである。
    GpsLocationListener.java
    TextViewPrintStream.java

位置情報 (1) 無線ネットワークを使った位置情報
http://blog.goo.ne.jp/marunomarunogoo/d/20111218


◆ レイアウト

□ res/layout/main.xml
---
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/startStopButton"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:onClick="onClickStartStopButton"
            android:text="@string/startButton" />

    </LinearLayout>

    <!-- プロバイダー名 -->
    <TextView
        android:id="@+id/provider_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
        
    <!-- センサー値 (1) -->
    <ScrollView
        android:id="@+id/sysoutScrollView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/sysout"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>
---

(1) センサー値を表示する部分は、コンソール表示のイメージ部分なので、つぎの既出の
ドキュメントを参照されたい。
    標準出力 - あて先に画面を追加する
    http://blog.goo.ne.jp/marunomarunogoo/d/20111222


◆ 文字列

□ res/values/strings.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Gps02</string>
    <string name="logTag">TEST</string>
    <string name="no_provider">プロバイダーは見つかりませんでした。</string>
    <string name="sensor_value_format_for_real">%1$s = %2.2f</string>
    <string name="sensor_value_format_for_timestamp">%1$s = %2$tF %2$tT</string>
    <string name="latitude">緯度</string>
    <string name="longitude">経度</string>
    <string name="accuracy">精度</string>
    <string name="altitude">標高</string>
    <string name="time">時間</string>
    <string name="speed">速度</string>
    <string name="bearing">ベアリング</string>
    <string name="startButton">開始</string>
    <string name="stopButton">終了</string>
    <string name="startMessageFormat">開始します。時間 = %1$tF %1$tT</string>
    <string name="stopMessageFormat">終了します。時間 = %1$tF %1$tT</string>
    <string name="providerDisable">無線ネットワークを使用できるようにしてください。</string>
    <string name="providerEnabled">無線ネットワークを使用します。</string>
</resources>
---


◆マニフェスト

以下の Gps01 プロジェクトと同じ。
位置情報 (1) 無線ネットワークを使った位置情報
http://blog.goo.ne.jp/marunomarunogoo/d/20111218