Android: Kotlin: タイマーを使ってViewを変更


Only the original thread that created a view hierarchy can touch its views.

タイマーで一定間隔おきに現在のボリュームをチェックして、その結果をViewに表示したかった。
KotlinではTimerの書き方が簡略化されているので、次のように書いてみた。


class MainActivity : AppCompatActivity() {

    override fun onResume() {
        Timer().schedule(1000, 1000) {
            // ボリュームを取得
            val ringVol = audio.getStreamVolume(AudioManager.STREAM_MUSIC)

            if (ringVol == 0) {
                // ミュート中
                linVolumeNotice.visibility = View.VISIBLE
            } else {
                linVolumeNotice.visibility = View.INVISIBLE
            }
        }
    }
}
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

オリジナルのスレッドからしかViewは変更できない。

タイマーの起動と停止

Handlerを使ってシングルスレッドにする。
とりあえずこれで出来た。


class MainActivity : AppCompatActivity() {
    private var mTimer: Timer? = null
    private val mHandler = Handler()
    
    override fun onResume() {
        val audio = getSystemService(Context.AUDIO_SERVICE) as AudioManager
        val linVolumeNotice = findViewById(R.id.linVolumeNotice) as LinearLayout
        mTimer = Timer()
        mTimer!!.schedule(object : TimerTask() {
            override fun run() {
                mHandler.post {
                    // ボリュームを取得
                    val ringVol = audio.getStreamVolume(AudioManager.STREAM_MUSIC)

                    if (ringVol == 0) {
                        // ミュート中
                        linVolumeNotice.visibility = View.VISIBLE
                    } else {
                        linVolumeNotice.visibility = View.INVISIBLE
                    }
                }
            }
        }, 1000, 1000)
    }
}

今回はOnResume()に書いたので、onPause()にタイマーをキャンセルする処理も必要になる。


    override fun onPause() {
         //タイマーの停止
         mTimer!!.cancel();
         mTimer = null;
    }

!!が多くて格好悪い。もっといい方法はないのか。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です