Help us understand the problem. What is going on with this article?

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

はじめに

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

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

  • 中央のボタンでスタート・ストップ
  • 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()

素材

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

kbcha10
スマホアプリが好きな学生です|おえかきもします
https://kbcha.myportfolio.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした