5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NCCAdvent Calendar 2019

Day 13

【Kotlin】Timerでメトロノームアプリを作る

Last updated at Posted at 2019-12-21

はじめに

メトロノームアプリを作る際に、テンポを毎回更新するところで少し詰まったので記事にしました。

作ったのはこんなアプリです。

  • 中央のボタンでスタート・ストップ
  • NumberPickerで速度調整(再生中の速度変更も可

音がないのでわかりづらいですが、スタートボタンを押すと表示されているテンポで音が流れます。

画面を作る

NumberPicker(id:tempoPicker)と再生・停止用にImageView(id:startButton)だけあれば大丈夫です。
(ボタンをImageViewで作ってます)

素材を入れる

スクリーンショット 2019-12-22 3.01.22.png
  • ボタンの画像を切り替えるための「start.png」「stop.png」
  • メトロノームのSE用の「metronome.mp3」

を入れておきます。SEは途中でカットされるので1秒以上あるものならなんでも大丈夫です。

コードを書く

MainActivity.ktに以下のコードを書いてください。

MainActivity.kt
package パッケージ名

import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.timer

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var tempo : Int = 40 //テンポ
        val handler : Handler = Handler() //タイマーで画面の変更が行われるときに必要
        var interval : Long = (60000/tempo).toLong() //テンポをインターバルに変換
        val mediaPlayer = MediaPlayer.create(this, R.raw.metronome)
        var metronome = timer(period = interval) {}
        var isPlaying : Boolean = false //メトロノームの再生状態(true -> 再生中, false -> 停止)

        tempoPicker.minValue = 40
        tempoPicker.maxValue = 208

        fun setMetronome() {
            interval = (60000/tempo).toLong()
            metronome.cancel()
            metronome = timer(period = interval) {
                handler.post {
                    mediaPlayer.stop()
                    mediaPlayer.prepare()
                    mediaPlayer.start()
                }
            }
        }

        tempoPicker.setOnValueChangedListener { picker, oldVal, newVal ->
            tempo = newVal
            if(isPlaying) setMetronome()
        }

        startButton.setOnClickListener {
            when(isPlaying) {
                true -> {
                    startButton.setImageResource(R.drawable.start)
                    metronome.cancel()
                    isPlaying = false
                }
                false -> {
                    setMetronome()
                    startButton.setImageResource(R.drawable.stop)
                    isPlaying = true
                }
            }
        }
    }
}

とりあえずこれで動きます。

詰まった点

Timer

タイマーの引数periodには待機時間が入ります。
今回のアプリでは、テンポの更新に伴って待機時間も連動するようにしたかったのですが、変数を指定しただけでは反映されませんでした。
なので、テンポが更新される度に下記のようにタイマーを再定義する必要があります。
また、metronome.cancel()を直前に入れないと新しいタイマーがどんどん生成されてしまうので注意です。

//更新する度に実行
metronome.cancel() 
metronome = timer(period = interval) {
    //定期的に実行したい命令
}

MediaPlayer

メトロノームのように、短いSEを定期的に鳴らす場合は一度前の音をstop()で停止し、prepare()start()の順で行う必要があります。

mediaPlayer.stop()
mediaPlayer.prepare()
mediaPlayer.start()

素材

スタート・ストップボタンの画像も作ったのでよければ使ってください。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?