【はじめてのKotlinプログラミング(26)】CountDownTimer(おまけ編:アラームを鳴らす)

前回前々回でカウントダウンタイマーを作ってみました。
スタートボタンを押すと、カウントダウンされ、
ストップで停止。
で、リスタートボタンで続きから開始されるというものでした。

今回はその続きで、ちょっとだけオマケで、このようにカウントが0になって終了したら、アラームを鳴らしてみたいと思います。アラーム音は、第23回のところで使ったMediaPlayerの復習になります。

使用音楽素材:効果音ラボさんのフリー素材「目覚まし時計のアラーム」

動画

コード

▼activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00:00"
        android:textSize="50sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="START"
        app:layout_constraintEnd_toStartOf="@+id/btnStop"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="STOP"
        app:layout_constraintEnd_toStartOf="@+id/btnRestart"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnStart"
        app:layout_constraintTop_toBottomOf="@+id/tv" />

    <Button
        android:id="@+id/btnRestart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="RESTART"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnStop"
        app:layout_constraintTop_toBottomOf="@+id/tv" />

    <Button
        android:id="@+id/btnReset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RESET"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

▼MainActivity.kt

package com.example.simplecountdown

import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    //8)横断的に使うやつを用意
    private lateinit var timer:CountDownTimer
    private var remainingTime:Long = 0 //9)残り時間(後で代入するので一旦0)
    private lateinit var mp:MediaPlayer //12)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //1)開始時間を設定
        val startTime:Long = 10000 //10秒

        //1)viewを取得+tvに開始時間を表示
        val tv:TextView =findViewById(R.id.tv)
        val btnStart:Button =findViewById(R.id.btnStart)
        val btnStop:Button =findViewById(R.id.btnStop)
        val btnRestart:Button =findViewById(R.id.btnRestart)
        val btnReset:Button =findViewById(R.id.btnReset)
        tv.text ="${startTime/1000}" //開始時間を秒で表示

        //2)ボタンの有効・無効
        btnStart.isEnabled = true
        btnStop.isEnabled = false
        btnRestart.isEnabled = false

        //4)カウントダウンタイマーのオブジェクトを用意
        //val timer =

        //3)スタートボタンを押したらカウントダウン
        btnStart.setOnClickListener {
            //5)「4」のtimerをスタート
            //timer.start()
            //10)startTimeでカウントダウン開始
            timer = countDownTimer(startTime).start()

            btnStart.isEnabled = false
            btnStop.isEnabled = true
            btnRestart.isEnabled = false
        }

        //6)ストップボタンを押した時の処理
        btnStop.setOnClickListener {
            timer.cancel()

            //ストップボタンを無効化
            btnStart.isEnabled = true
            btnStop.isEnabled = false
            btnRestart.isEnabled = true
        }

        //7)リスタートボタンを押した時の処理
        btnRestart.setOnClickListener {
            //10)remainingTimeでカウントダウン開始
            timer = countDownTimer(remainingTime).start()

            btnStart.isEnabled = false
            btnStop.isEnabled = true
            btnRestart.isEnabled = false
        }

        //11)リセットボタン
        btnReset.setOnClickListener {
            timer.cancel()
            remainingTime = 0
            tv.text ="${startTime/1000}" //開始時間を秒で表示

            //2)ボタンの有効・無効
            btnStart.isEnabled = true
            btnStop.isEnabled = false
            btnRestart.isEnabled = false

            //14)mp停止
            mp.stop()
            mp.release()
        }
    }

    //8)「4」を関数でここに用意
    private fun countDownTimer(st:Long):CountDownTimer{
        val tv:TextView =findViewById(R.id.tv)
        val btnStart:Button =findViewById(R.id.btnStart)
        val btnStop:Button =findViewById(R.id.btnStop)
        val btnRestart:Button =findViewById(R.id.btnRestart)

        return object :CountDownTimer(st,100){
            //[4-1]途中経過・残り時間
            override fun onTick(p0: Long) {
                //TODO("Not yet implemented")
                //残り時間を表示
                tv.text ="${p0/1000}" //秒単位
                remainingTime = p0 //9)残り時間を代入
            }
            //[4-2]終了設定
            override fun onFinish() {
                //TODO("Not yet implemented")
                tv.text ="タイムアップ"

                //終了時は3つ全部無効
                btnStart.isEnabled = false
                btnStop.isEnabled = false
                btnRestart.isEnabled = false

                //13)MediaPlayerを再生
                mp = MediaPlayer.create(this@MainActivity,R.raw.alarm)
                mp.isLooping =true
                mp.start()
            }

        }
    }
}

テキスト

それでは音楽ファイルが用意できたらはじめて行きましょう。

一応、復習がてらMediaPlayerの使い方について改めて解説しておくと

①まずres、リソースの中にrawフォルダを作って、その中に音楽ファイルを入れる
②MediaPlayerをインポート
③最後に鳴らしたいところに開始の記述を書く

という流れになります。

12)rawとMediaPlayer

それではまずrawフォルダを用意して、その中に音楽ファイルを入れていきましょう。
てことで、左側のres、リソースを開いてください。

rawフォルダは、最初の段階では用意されていないので
使いたい人が、使いたい時に作ってあげます。
resを右クリック⇒new⇒で、フォルダなんですけどプログラミングではちょっとカッコつけて
ディレクトリといいます

raw と入力してエンター。

これでrawフォルダが出来ましたので、みなさんご用意の音楽ファイルを
入れてください。
ここではファイル名をalarmとしています。

このままドラッグ&ドロップでもいいですし
ctrlを押しながらするとコピーになります。

はいこれでrawフォルダに音楽ファイルが入りました。

ではMediaPlayerをインポートしましょう。

場所はonCreateの上、厳密には クラスの下。

ここにprivate valで宣言してとりあえず名前をつけてあげます。
MediaPlayer略してmp

これに続けてMediaって入れるとMediaPlayerってのがあるので選択。
そうするとMediaPlayerがインポートされて、この人 mp は
MediaPlayerのクラスが使えるようになりました。

ただし、今は名前を付けただけで、本当はこっちにイコールで中身を入れてあげなければいけません。

今はここでは名前だけつけて、中身はonCreateの中で書いていくので、宣言する場所と、中身の場所が違うと。
てことで、中身は後でね、って言う場合は
前回説明のとおり
初期化を遅らせるということでlateinitを使うんでしたね。

private につづけて
lateinitの場合は、valではなくってvar。

これで下準備は完了です。

次にmpを必要なところに書いていきましょう。

    private lateinit var mp :MediaPlayer //12)

13)mpと、start()

それでは、今名前を付けたmpを使って、メディアプレイヤーを再生していきましょう。

音楽を鳴らすタイミングは、もちろんタイマーが終わった時なので、

CountDownTimerの、onFinishの方ですね。

この波カッコの中に書いていきましょう。

mp= MediaPlayer.create~

ここではじめて中身が入ります。

で、前回はここに自分自身ということでthis、とかいて
続けてファイルの場所であるR.raw.alarm と書かいてくださいって説明したと思うんですが。

今回なんかエラーが出ています。

None of the following functions can be called with the arguments supplied

これ、javaをやってた方はピンと来るかもしれないんですけど、
あー、ここは、MainActivity.thisね、と。それで正解ちゃ正解なんですけど、kotlinは書き方だけちょっと違います。

正解を言っておくと

this@MainActivity

と記述します。まず、一から、ちょっと理屈っぽく説明しますね。

そもそもこのthisっていうのは自分自身という意味なんですけど
厳密には「自身のクラス」という意味です。
てことで、今回の場合のthisは、このカウントダウンタイマーのクラスをさすので
画面全体を指定する場合には@(アットマーク)に続けてMainActivityを書いてあげる必要がります。

てことで、ひとまず理屈っぽく説明すると以上になります。

が、ぶっちゃけ、よーわからんなって人はですね
thisって書くところは、「thisか、this@アクティビティ名」、
この2択でなんとかなります。

てことで、これで無事、mpの中に色々入りましたのでもう少し書いていきましょう。

まず1つ目は1曲目が終わったらループをさせたいので

mp.isLooping = true

あとはスタートさせてあげればいいので

mp.start()

これで音楽は流れるようになりました。

ただし今は音楽が流れっぱなしなので、
リセットボタンを押したら音楽を止めてあげましょう。

14)MediaPlayerを停止

アラーム音を停止するのは
リセットボタンが押された時なので
btnResetの波カッコの中に書いていきます。

            //14)mp停止
            mp.stop()
            mp.release()

まずは音楽を止めて
mp.stop()

メモリを解放します
mp.release()

さあ、これで完成です。
エミュレータを起動して、アラーム音がなるか、確認してみましょう。

 

 

    コメントを残す