CountDownTimerで周期的にイベント発生
周期的(10秒ごとなど)にイベントを発生させたい場合はCountDownTimerというクラスを使います。複数起動でき、カウントする時間間隔も設定することが可能です。
カウントタイマーの使い方
では、その使い方を紹介します。以下のコードは10秒間カウントし、残り時間をテキストビューに表示するような例です。
public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final TextView countText = (TextView)findViewById(R.id.countText); //タイマーを設定 CountDownTimer cdt = new CountDownTimer(10000, 1000) { public void onTick(long millisUntilFinished) { countText.setText(Long.toString(millisUntilFinished / 1000)); //残り時間を表示 } public void onFinish() { countText.setText("終了"); } }.start(); } }
CountDownTimerのコンストラクタには(カウントする時間, カウントする間隔)の順でlong型を渡します。時間の単位はミリ秒なので30秒間を1秒間隔でカウントする場合には
new CountDownTimer(30000, 1000)
のようにしてインスタンスを生成します。
インスタンスの生成後はstartメソッドを呼び出すことでタイマーが開始されます。
開始後は指定した秒数ごとにonTickが呼ばれ、カウントが0になった時点でonFinishが呼び出されます。
また、もしタイマーを途中で中止したい場合は
cdt.cancel();
こののようにcancelメソッドを呼び出し、カウントを完全に終了させます。
タイマーを中止させる
タイマーを中止するにはcancelメソッドが使えるのですが、CountDownTimerにはカウントを途中でストップしたり、再開したりするための機能がありません。
ちなみにcancelを呼び出した後にstartを再び実行すると初期の状態に戻ったままタイマーがスタートしてしまいます。
そこで、タイマーを途中でストップして再開できるようにCountDownTimerクラスを以下のように拡張しました。
public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button stopButton = findViewById(R.id.stopButton); Button startButton = findViewById(R.id.startButton); MyCountTimer timer = new MyCountTimer(30000, 1000); //一時停止ボタンのイベント stopButton.setOnClickListener(new View.onClickListener() { public void onClick(View v) { final long count = timer.getCount(); //ストップした時間の取得 timer.cancel(); //再開ボタンのイベント startButton.setOnClickListener(new View.onClickListener() { public void onClick(View v) { timer = new MyCountTimer(count, 1000); timer.start(); } }); } }); } //独自カウントダウンタイマークラス class MyCountTimer extends CountDownTimer { public long countMillis = -1; public MyCountTimer(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); millis = millisInFuture; //残り時間を保存 } public void onTick(long millisUntilFinished) { final TextView countText = (TextView)findViewById(R.id.countText); //残りミリ秒を表示するテキストビュー countText.setText(Long.toString(millisUntilFinished)); } public void onFinish() {} //残り時間を返す。 public final Long getCount() { return countMillis; } } }
CountDownTimerを拡張してMyCountTimerを作り、countMillisというメンバ変数にこれまで経過したミリ秒を保存しています。
そして、ボタンでタイマーを一時停止して再開するときに独自に作ったgetCountメソッドで残り時間を取得し、新たにタイマーを作成しています。ただし、cancelが呼ばれる前に呼び出さないと保存した情報を取得できません。
いちいちタイマーを作成するのは面倒ですが、CountDownTimerのstartメソッドはfinal関数になっていて再定義できないのでこのような方法をとりました。
カウントダウンタイマーの誤差
カウントダウンタイマーには誤差があり、秒単位でカウントする場合はそのせいで表示されない秒がでることがあります。たとえば次のようなログ表示をonTickメソッドで実行し、残りのミリ秒を観察してみます。
public void onTick(long millisUntilFinished) { Log.i("MainActivity : ", "残り時間 = " + Long.toString(millisUntilFinished)); }
そして、次が1秒間隔で30秒間タイマーを動かしたときの最後の5秒間のログです。
11-01 22:04:54.770: I/millis =(1132): 5044 11-01 22:04:54.770: I/millis =(1132): 4021 11-01 22:04:54.770: I/millis =(1132): 2991 11-01 22:04:54.770: I/millis =(1132): 1963
3秒(3000ミリ秒)の部分だけカウントされていません。
この誤差を解消するにはカウント間隔を1000ミリ秒ではなくもっと短い500ミリ秒などに設定することで上手くいきます。