一時停止と状態遷移
一通りメディアの再生ができたところで、一時停止を実装していきましょう。
一時停止
一時停止も再生も同じボタンを使っていますので、onPlayButtonClick()に処理を記述します。このメソッド内で、すでに再生中かどうかの判定を行っています。このifブロックの下にelseブロックを追加し、そこに一時停止処理を記述します。以下のソースコードを追記してください。
public void onPlayButtonClick(View view) { if(!_player.isPlaying()) { ~省略~ } else { _player.pause(); // (1) _btPlay.setText(R.string.bt_play_play); // (2) } }
再生の一時停止を行うメソッドはpause()です。これを実行しているのが(1)です。その後、ボタンの表記を「再生」に変更します。それが(2)です。
この状態で、アプリを再起動して、再生ボタンを押してください。メディア再生中に「一時停止」ボタンを押してください。再生がストップし、ボタン表記が「再生」に戻ります。もう一度再生ボタンを押すと、続きから再生されることが確認できます。
状態遷移
ところで、リスト5の(1)ではpause()メソッドを使用していますが、なぜstop()メソッドではないのでしょうか。下図を参照してください。
これはMediaPlayerクラスのAPI仕様書ページに掲載されている図で、MediaPlayerオブジェクトの状態遷移図です。この図の青い長円が状態を表し、矢印がその状態へ遷移するメソッドです。矢印の先が2重になっているのがリスナを表します。
ここで注目したいのは、再生中である「Started」状態からstop()メソッドを実行すれば「Stopped」状態に移行しますが、その「Stopped」状態からもう一度「Started」状態へ遷移する矢印がないことです。「Stopped」状態からもう一度再生を行うには、「Prepared」状態を経ること、つまり、再生準備をもう一度行わなければならないということです。そのため、単純に再生を停止するには、pause()メソッドを使用します。
一方で、再生が完全に終了した状態は、「PlaybackCompleted」です。この状態から再度「Started」状態への遷移、つまり、再生を開始するケースには矢印が存在し、start()メソッドを実行すればいいことが分かります。
戻る・進むボタン
再生、一時停止が実装できたので、次は戻る、進むを実装していきましょう。戻るボタンタップ時の処理は、今再生中のメディアファイルの最初から再生し直す処理です。一方、進むボタンタップ時の処理は、次のトラックを再生するのが普通です。ところが、今回のサンプルは1トラックのみなので、メディアファイルの最後までスキップする処理を実装することにします。
戻る
戻るボタンの処理メソッドはonBackButtonClick()なので、このメソッドを追加します。以下のソースコードを追記してください。
public void onBackButtonClick(View view) { _player.seekTo(0); }
メソッド内は1行で、MediaPlayerクラスのseekTo()メソッドを実行しています。seekTo()は再生位置を指定できるメソッドです。引数として再生位置をミリセカンドで指定します。ここでは「0」、つまり、最初を指定しています。
さらに、seekTo()は、MediaPlayerオブジェクトが再生中の場合、指定の開始位置まで移動した上で自動で再生してくれます。停止中の場合は指定位置まで移動した上、停止したままでいてくれます。
ここまでで一度アプリを再起動し、戻るボタンがちゃんと機能するかを確認してみてください。再生中はもちろん、一時停止中も戻るボタンで再生位置が最初に戻ることを確認してください。
進む
では、同じ要領で進むボタンを実装しましょう。以下のソースコードを追記して、onForwardButtonClick()を追加してください。
public void onForwardButtonClick(View view) { int duration = _player.getDuration(); // (1) _player.seekTo(duration); // (2) if(!_player.isPlaying()) { // (3) _player.start(); // (3) } }
進むボタン処理は、戻るボタンと同様にseekTo()メソッドを使います。ただ、再生位置のミリセカンドが分かりません。そこを取得しているのが(1)です。MediaPlayerクラスのgetDuration()メソッドは、現在再生中のメディアファイルの長さを取得できます。ただし、ストリーミングなど長さの取得が不可能なものは-1が返ってきます。この戻り値を使って、再生位置を最後にします。それが(2)です。
では、(3)はどんな処理なのでしょうか。
戻るの項で説明したように、seekTo()は、MediaPlayerオブジェクトが再生中の場合、指定の開始位置まで移動した上で自動再生してくれます。したがって、開始位置を最後にした場合はそこから再生が始まり、次の瞬間再生が終了し、PlayerCompletionListenerが呼び出されます。
ところが、再生が停止中の場合は、開始位置が最後まで移動するだけで、再生が開始されません。そのため、再生が終了する一歩手前で止まったままなのです。したがって、PlayerCompletionListenerが呼び出されません。この状態で再生ボタンを押すと、一瞬だけ再生になりすぐに終了してしまうという、不自然な挙動になってしまいます。これを避けるために、再生を開始し、終了まで持っていく処理を行っているのが(3)です。
ここまでで一度アプリを再起動し、進むボタンがちゃんと機能するかを確認してみてください。特に、一時停止中の進むボタンの挙動を確認してください。