どうも!LSSです!
KotlinでのAndroidアプリ開発、今回は、腕時計によくついてる(?)カウントダウンタイマーとストップウォッチを、一画面で同時に表示しちゃうアプリを作ってみます。
今回の肝となる部分を先に書いておきます。
カレンダーのインスタンスを作成
var cld0=Calendar.getInstance()
カレンダーのインスタンスに、現在時刻の値をミリ秒単位で設定
cld0.timeInMillis=System.currentTimeMillis()
あ、「cld0」ってのは例によって「任意の名前」です。
好きに名付けちゃってOK!
これで設定されるcld0.timeInMillisはLong型(Int型よりも大きい数値を表現できる整数型)になります。
※実際に入る値は「エポックからの経過時間」と言われるもので、
1970年1月1日 00:00:00から経過したミリ秒数、という結構大きい値が入りますw
今回は時間の差分を計算するだけなのであまり意識しなくて済みます^^
何の役に立つの?
ほら、よくカップラーメン作る時とか、3分タイマーをセットしてピピピピって鳴った時、少し手が離せなくてちょっと時間が過ぎてしまったか?って思う事、ありませんか?(ねーよwって思った人も、この際ある事にしておいてw)
百均で売ってるキッチンタイマーや、腕時計のカウントダウンタイマー機能は時間が過ぎたら0になるだけで、それからどれだけ時間が過ぎたかは教えてくれません。
腕時計には別にストップウォッチ機能がついてたりもしますが、別画面の別機能で併用はできません。(いや、できなくもないか?どっちにしろ面倒なので出来ない事にして強引に話を進めます。)
本当の目的
はい、無理やりな?理由づけも無理やり過ぎましたねw
えっと、本当はアプリ上で時間を扱う練習、です。
これもまた何通りかやり方あるみたいですが、今回はCalenderクラスを使用して、都度、現在時刻を取得して、計測開始時刻との差を計算して表示する方法でいきます。
以前の記事で、
タイマー処理=一定周期で処理を行う、てのをkotlinで極力カンタンに書いてみる(Handler利用)
にて解説したHandlerタイマーも使用します。
「現在時刻とか取得しなくても、HandlerのpostDelayedで100ミリ秒で指定したなら、カウント取るだけでいいんじゃないの?」
という考え方もありますが、あれは結構誤差も出るらしくて、わずかな誤差でも積み上げてのカウントとなると大きくズレるかも知れない??って事で、堅実な方法として「都度、時刻取得」という方法をとります。
画面デザイン
↑こんな風にシンプルなデザインにしてみました。
コード
以下、kotlinのコード全文です。
珍しく//コメント書きまくってみました。
※importは割愛しています。
class timerActivity : AppCompatActivity() {
var cld0=Calendar.getInstance() // 計測開始時の時刻値 保管用
var cld1=Calendar.getInstance() // 現在の時刻値 保管用
var tflag=false // タイマー動作中はtrueになるフラグ
var CntT=180 // カウントダウン用の時間を秒で指定
val hnd0=Handler()
val rnb0=object:Runnable{
override fun run() {
// 現在の時刻値をミリ秒で取得
cld1.timeInMillis=System.currentTimeMillis()
// カウントダウンの表示処理
var i=CntT+((cld0.timeInMillis-cld1.timeInMillis)/1000).toInt()
if (i<0){i=0}
tvCntD.text="%02d:%02d".format(i/60,i%60)
// ストップウォッチの表示処理
tv0.text="%.2f".format((cld1.timeInMillis-cld0.timeInMillis).toFloat()/1000)
// 0.01秒後にこの処理を再度呼び出し
hnd0.postDelayed(this,10)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_timer)
}
// START・STOPボタンを押した時の処理
fun btSclk(v:View){
// tflagはタイマー作動中はtrue、停止中はfalse
if (tflag) {
// 作動中にSTOPが押された場合の処理
tflag=false
btS.text="START"
//handlerタイマー処理をキャンセル
hnd0.removeCallbacks(rnb0)
}else{
// 停止中にSTARTが押された時の処理
tflag=true
btS.text="STOP"
//STARTが押された時の時刻値をcld0にセットし、handlerタイマー処理を開始
cld0.timeInMillis=System.currentTimeMillis()
hnd0.post(rnb0)
}
}
override fun onDestroy() {
// Actibityが破棄される時に、先にhandlerタイマー処理をキャンセル
hnd0.removeCallbacks(rnb0)
super.onDestroy()
}
}
一応、xmlも書いておきます。
↓こんな感じです。文字列は別ファイルから読みだしていますが。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
android:orientation="vertical"
tools:context=".timerActivity">
<TextView
android:id="@+id/tvCntD"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/t0300"
android:textSize="72sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/btS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btSclk"
android:text="@string/btStart"
android:textSize="36sp" />
</LinearLayout>
<TextView
android:id="@+id/tv0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/zero3"
android:textSize="36sp" />
</LinearLayout>
こんな感じに動作します
残り時間の表示と、経過時間を細かく表示してくれていますね^^
とりあえず出来ましたが、
カウントダウン終了時に何のリアクションも無いので完成品とは言えませんね^^;
ここから更に付与するとしたら
- カウントダウン終了時に音と振動でお知らせ
- タイマー動作中にAndroid側の機能で勝手に終わってしまわないようにする
- ストップウォッチにLAP計測機能をつける
といったところでしょうか。
では、今回はこのへんで!!
次回またよろしくお願いいたします^^