ストップウォッチを作ろう

Handlerとタイマー日付を扱う、Date入門を組み合わせて、 さらに文字列のフォーマットというのを組み合わせると ストップウォッチのようなアプリを作る事が出来ます。

簡単な方針を書いておくので、チャレンジしてみてください。

作りたいもの概要

ボタンは二つ、スタートとリセットがある。 スタートは開始している時は一時停止にもなる。

UIとしては、

  • 数字を表示するTextView
  • スタートボタン
  • リセットボタン

の三つとなる。下二つのボタンは横に並べる方がそれっぽいかも?(これは好みで)

TextViewは大きめにしておくと良いでしょう。

アプリの状態を考える

こういうアプリを考える時には、アプリは何種類の状態があるかを考えておくのが良いでしょう。

  1. 一番最初の状態
  2. ストップウォッチがスタートして秒数が進んでいく状態
  3. ストップウォッチが一時停止している状態

だいたいこの三つの状態を考えれば十分でしょう。 二番目の状態を「スタート状態」と呼ぶ事にします。

スタートボタンのとりあえずの処理

再開を考えると少し難しくなるので、まずは再開を考えない簡単な実装で進めて、後で直します。

まずはスタートボタンが押されたらその時点の時間を覚えて、タイマーを100msecで開始しましょう。

時間を覚えるにはLongでないといけないので、変数は

var kaisiJikan : Long = 0

のように型指定をしないといけないと思います。

タイマーの設定

タイマーがきたら表示の時間を更新し、スタート状態だったら次のタイマーを設定します。 タイマーは100 msecごとに呼べば良いでしょう。

時間の計算

理論上はタイマーは毎回100msecごとに来るのだから表示を100msecずつ進めればいいのだけれど、 タイマーはたまに遅れてやってきたりもするので、このやり方では時間はずれていってしまいます。

より正確には、スタートが押された時点でのDate()のtimeプロパティを使ってその時点の時間を覚えておいて、 各時間でDate()を呼んでスタートからどれけ時間が経過したのかを計算して、そこから文字列を出力するといいでしょう。

まずはmsecを単純にTextViewに表示して、うまく動くのを確認したら次の「表示する文字列の作り方」に挑戦してみてください。

表示する文字列の作り方(難しい)

さて、うまい事スタートを押してから何msecが経ったのかを計算出来たとします。 これから、

03:12 64

のような表示を作りたい。

分かるのはmsecです。例えば1000msecなら1秒となるので、

00:01 00

と表示したい。100msecなら

00:00 10

です。

5900msecなら

00:59 00

です。 では6000msecならどうなるでしょうか?以下のようになる?

00:60 00

いえ、普通はストップウォッチはそうはなりません。(実際にストップウォッチアプリなどで試してみてください)

という事でmsecから以下を計算する必要がありそうです。

  • 何分か(秒は切り捨て)
  • 何秒か(分は取り除いた残り)
  • 何msecか

例えば100000msec、つまり100秒の時は、

  • 1分
  • 40秒
  • 0msec

という事を計算で知る必要がある。

それぞれ関数にしてみましょう。 分をローマ字にするとfunで関数のfunとかぶってしまうので、 名前は英語にしましょう。

minutes, seconds, millisecondsでいいでしょう。

minutes関数を作る

何分かを計算する方法を考えます。 100秒なら1分40秒なので1、200秒なら3分20秒なので3となるようにしたい。

0 == minutes(10000)
1 == minutes(100000)
3 == minutes(200000)
4 == minutes(250000)

どうやって計算したらいいでしょうか? ちなみに時間はLongで処理するので引数はLongにしておきます。 returnはIntにした方がいいですがどっちでもいいです。 LongからIntにするのはtoInt()でいいでしょう。

fun minutes(msec: Long) : Int { // ここをどうにかする return 0 } fun main() { println(minutes(10000)) // 0になって欲しい println(minutes(100000)) // 1になって欲しい println(minutes(200000)) // 3になって欲しい println(minutes(250000)) // 4になって欲しい }

seconds関数を作る

minutes関数を作ったら、残りの秒数を求める関数を作ります。 100秒なら1分40秒なので40、200秒なら3分20秒なので20となるようにしたい。

方針はいろいろありますが、

  • minutes関数で作ったminutesのぶんを引く
  • msecは切り捨てる(割る/を使えば良さそう)

というのが簡単でしょうか。

fun seconds(msec: Long) : Int { // ここをどうにかする return 0 } fun main() { println(seconds(500)) // 0になって欲しい println(seconds(10000)) // 10になって欲しい println(seconds(100000)) // 40になって欲しい println(seconds(200000)) // 20になって欲しい println(seconds(250000)) // 10になって欲しい }

milliseconds関数を作る

milliseconds関数を作ります。 これはここまで求めたものを全部引いてもいいのだけれど、 割った余り(%)を使ってもいいでしょう。

どういう値の時に幾つになって欲しいのかを自分で考えて3種類くらい書いて、 それを計算する方法を考えます。 算数の問題みたいですね。算数苦手だと少しむずかしいかも。 わからんかったら聞いてください。

以上の関数を使って時間を表す文字列をとりあえず作る

だいたい以下みたいな感じで、目的の文字列がほぼ出来ると思います。


val someMsec:Long = 1234567

val mins = minutes(someMsec)
val secs = seconds(someMsec)
val msecs = milliseconds(someMsec)

println("${mins}:${secs} ${msecs}")

これだと5秒の時に05では無く5になってしまいますが、まずはこれでいいでしょう。

これでTextViewにセットすればでこぼこした感じにはなるけれどストップウォッチが出来ると思います。

文字列のフォーマットを使って05とかを処理する

文字列のフォーマットの内容を参考に、 分、秒、msecを0でパディングします。

これで表示が正しくなりました。

一時停止と再開の処理

普通ストップウォッチは時間が進んでいる時にスタートボタンを押すと一時停止します。 もう一度スタートボタンを押すと再開します。 この処理を実装しましょう。

再開する時には覚えている時間を一時停止していた期間分進める必要があります。 これはちょっとむずかしい。

状態の区別

再開を実現する為には、スタートボタンが押された時に、現在が

  • 開始状態
  • 一時停止状態

のどちらかを判定する必要があると思います。

やり方は幾つかあります。

  • 覚えている時間が-1なら開始状態とする
  • 現在が開始状態か一時停止状態かを区別するBoolean変数を用意して適切に設定する

どちらでも良いと思います。

一時停止していた時間ぶんだけ最初の時間をずらす

再開したときに一時停止していたぶんの時間が加算されないようにするには、 開始の時間を一時停止していた時間ぶんだけかさ増しすれば良いでしょう。

その為には一時停止が始まった時の時間を覚えておく必要があるでしょう。

再開の時には、一時停止された時から再開された時までの時間を開始の時間に足してやれば良いと思います。

リセットとかenabledとかをいろいろ設定

開始されてない時にリセット出来ないようにしたり、 開始中はスタートボタンのtextをポーズに変えたり、 そういう細々した事を整備して完成です。