はじめに
こんにちは、@rs_tukkiです。
アドベントカレンダーに限らず、期限の決まっているタスクというものはその期限が近づいてきて初めて焦りが沸いてくるものです。
実際、私はこの記事を担当日が終わるギリギリに書いています。去年の反省が全く生きていない
しかし、そうやってダラダラ後回しにしてタスクを溜め続けていると、焦りを通り越して
引用元:存在しない漫画の1コマbot様
やらなきゃいけないことがいつの間にかなくなってくれないかな といった半ば諦めの思考に陥りがちです。
...今回は、そんな思いを実現するためのアプリを作りました。
今まで作ったクソアプリ
作ったもの
今回は、Androidで動作するシンプルなTodoアプリを作成しました。
任意のタスク内容と期限を入力すると、新しいタスクが画面下部のリストに追加されます。
追加したタスクは完了したらゴミ箱ボタンをタップして消すことも出来ます。
そして、タスクの登録日を起点として期限が近づくたびにタスクは赤色で潰れていき、
期限を過ぎると消えて無くなる、つまり自動的に削除されてしまいます。
(また、タスクは期限が遠いものほど上に表示されます。現実から目を逸らしやすい)
もしこのアプリを使ってみたい奇特な方がいれば以下からどうぞ。
最新版のAndroid Studioでデバッグ実行すれば使えます。
主な技術スタック
- targetSdk: 36 (Android16)
- Kotlin: 2.1.0
- Jetpack Compose BOM: 2025.12.00
- Room: 2.8.4
- Material Design 3
特に変わったことはせず、Roomでタスクを管理、Jetpack Composeで画面表示を行っています。
タスクの自動削除は当初WorkManagerを用いて日次で削除処理を走らせることも検討しましたが、
バックグラウンドでの実行が必須というわけではなく、ViewModelの初期化処理で毎回走らせる方が実装コストが安いためWorkManagerは使いませんでした。
主な実装
実装自体は日付と内容を入力してDBに登録し表示するというだけのシンプルなものではありますが、
それでも苦労した点を2つほど残しておきます。
消えるまでの残り日数を可視化する
今回は登録したタスクがだんだんと赤色で潰れていく状態を可視化したかったので、
タスクの登録日を0, 期限を100として現在どの位置にいるかを割合で示す計算をしています。
fun calculateProgress(
createdAt: Long,
deadline: Long,
now: Long = System.currentTimeMillis()
): Float {
if (now >= deadline) return 1f
return ((now - createdAt).toFloat() / (deadline - createdAt).toFloat())
.coerceIn(0f, 1f)
}
タスクの期限はDatePickerで選択した日付の23:59:59としたかった(=期日が終わるまでは期限内として扱われる)のですが、selectedDateMillis自体はエポックミリ秒を示すため、
一度UTCのLocalDate型に変換する
↓
時刻を23:59:59にする
↓
システムデフォルトのタイムゾーンを指定する
↓
エポックミリ秒に戻す
という手順を踏む必要があります。
selectedDateMillis = datePickerState.selectedDateMillis?.let { millis ->
Instant.ofEpochMilli(millis)
.atZone(ZoneOffset.UTC)
.toLocalDate()
.atTime(23, 59, 59)
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
}
これに気付かず思いのほか時間を使ってしまいました...
計算した割合分タスクを色分けする
タスク1つ1つの枠組みはBoxで表現しています。
当初は1つのBoxの一定割合だけ背景色を変更できないか考えていましたが、ここはBoxを2つ重ねた上で
- 上:進捗の割合と同じだけの横幅を持った赤色のBox
- 下:全面を覆う薄い色のBox
とすることで、進捗している分だけ赤色が現れ、下の色が隠れていくという実装になっています。
Box(
modifier = Modifier
.matchParentSize()
.background(MaterialTheme.colorScheme.surfaceVariant)
) {
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(progress)
.background(
MaterialTheme.colorScheme.error.copy(alpha = 0.3f)
)
)
}
まとめ
と言うわけで、なんとか担当日が過ぎて潰れて消えてしまう前に記事を投稿できました。
期限のあるタスクはきちんと期限内に終わらせましょう。
やらなきゃいけないことを後回しにしたとき最終的に困るのは自分自身です。



