【Androidアプリ開発】DatePickerDialogとTimePickerDialog

2021年4月10日

前回はコンテキストメニューの実装を行いました。

【Androidアプリ開発】コンテキストメニューを表示する
【Androidアプリ開発】コンテキストメニューを表示する
前回はAVDで日本語入力の手順を紹介しました。 今回は、画面のアイテムを長押しした時に表示されるコンテキストメ…
http://nomux2.net/post-2292/

今回はDatePickerDialogとTimePickerDialogを説明しようと思います。

DatePickerDialogを実装する

DatePickerを実装するだけなら簡単に行うことができます。以下にサンプルのソースを表示します。

まずはレイアウトファイルです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".TodoUpdateActivity">

    (省略)

    <Button
        android:id="@+id/btLimitDate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/update_todo_bt_limit_date"
        android:onClick="onClickLimitDate"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvLimit" />

</androidx.constraintlayout.widget.ConstraintLayout>

続いてDatePickerDialogの呼び出しと処理です。

package com.example.todomanager

import android.app.Activity
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import java.time.LocalDateTime
import java.util.*


class TodoUpdateActivity : AppCompatActivity() {

    (省略)

    fun onClickLimitDate(view :View)
    {
        //本日の日付を取得
        val dateNow = LocalDateTime.now()

        //年、月、日を設定
        val year = dateNow.year
        //val month = dateNow.month.ordinal
        val month = dateNow.monthValue - 1
        val day = dateNow.dayOfMonth
        //ボタンを指定なしにする(キャンセルを押されたときに初期化された状態にするため)
        val btLimitDate = view as Button
        btLimitDate.setText(R.string.update_todo_bt_limit_date)
        //カレンダーダイアログを作成して、表示する
        val calendar = DatePickerDialog(this, DialogDateButtonClickLister(), year, month, day)
        calendar.show()
    }

    private inner class DialogDateButtonClickLister : DatePickerDialog.OnDateSetListener {
        override fun onDateSet(p0: DatePicker, year: Int, month: Int, day: Int) {
            //日付を整形する
            val dateString = String.format("%04d/%02d/%02d", year, month + 1, day)
            //日付を表示するViewを取得
            val btLimitDate = findViewById<Button>(R.id.btLimitDate)
            //ボタンのテキストを変更
            btLimitDate.text = dateString

        }
    }
}

これを実行すると以下のようになります。

カレンダーダイアログの表示

Dialogを作成してshow関数で呼び出すだけで表示できます。
ただし、キャンセルを押した場合の処理は指定できません。

キャンセルボタンの処理を記述する場合、後述のDialogFragmentを使用してください。

//カレンダーダイアログを作成して、表示する
val calendar = DatePickerDialog(this, DialogDateButtonClickLister(), year, month, day)
calendar.show()

リスナを作成

リスナを設定します。onDateSetはカレンダーダイアログでOKをクリックした際に呼び出されます。

private inner class DialogDateButtonClickLister : DatePickerDialog.OnDateSetListener {
    override fun onDateSet(p0: DatePicker, year: Int, month: Int, day: Int) {
        //日付を整形する
        val dateString = String.format("%04d/%02d/%02d", year, month + 1, day)
        //日付を表示するViewを取得
        val btLimitDate = findViewById<Button>(R.id.btLimitDate)
        //ボタンのテキストを変更
        btLimitDate.text = dateString
    }
}

TimePickerDialogを実装する

レイアウトファイルです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".TodoUpdateActivity">

    (省略)

    <Button
        android:id="@+id/btLimitTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:onClick="onClickLimitTime"
        android:text="@string/update_todo_bt_limit_time"
        app:layout_constraintStart_toEndOf="@+id/btLimitDate"
        app:layout_constraintTop_toTopOf="@+id/btLimitDate" />

</androidx.constraintlayout.widget.ConstraintLayout>

続いてTimePickerDialogの呼び出しと処理です。

package com.example.todomanager

import android.app.Activity
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import java.time.LocalDateTime
import java.util.*


class TodoUpdateActivity : AppCompatActivity() {

    (省略)

    fun onClickLimitTime(view :View)
    {
        //本日の日付を取得
        val timeNow = LocalDateTime.now()

        val btLimitTime = view as Button
        btLimitTime.setText(R.string.update_todo_bt_limit_time)

        val calendar = TimePickerDialog(this, DialogTimeButtonClickLister(), timeNow.hour, timeNow.minute, true)
        calendar.show()

    }

    fun onClickLimitTime(view :View)
    {
        //現在の時刻を取得
        val timeNow = LocalDateTime.now()

        val btLimitTime = view as Button
        btLimitTime.setText(R.string.update_todo_bt_limit_time)

        val timeDialog = TimePickerDialog(this, DialogTimeButtonClickLister(), timeNow.hour, timeNow.minute, true)
        timeDialog.show()

    }
}

時計ダイアログを表示

基本的にカレンダーと同様です。
Dialogを作成してshow関数で呼び出すだけで表示できます。
ただし、キャンセルを押した場合の処理は指定できません。

キャンセルボタンの処理を記述する場合、後述のDialogFragmentを使用してください。

val timeDialog = TimePickerDialog(this, DialogTimeButtonClickLister(), timeNow.hour, timeNow.minute, true)
timeDialog.show()

リスナを作成

リスナを設定します。onTimeSetは時計ダイアログでOKをクリックした際に呼び出されます。

private inner class DialogTimeButtonClickLister : TimePickerDialog.OnTimeSetListener {
    override fun onTimeSet(p0: TimePicker?, hour: Int, min: Int) {

        val timeString = String.format("%02d:%02d", hour, min)

        val btLimitTime = findViewById<Button>(R.id.btLimitTime)

        btLimitTime.text = timeString
    }
}

DialogFragmentを使用して表示

新しくDialogFragmentクラスを作成します。

Kindは「Class」を選択します。

クラスが作成されると「java」フォルダの下にファイルが作成され以下の記述がされています。

package com.example.todomanager
class TodoDatePickerDialogFragment
}

これを以下のように書き換えます。

package com.example.todomanager
class TodoDatePickerDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialogDatePicer = DatePickerDialog(context!!, DialogDateButtonClickLister(), 2020, 3, 27)
        return dialogDatePicer
    }

    override fun onCancel(dialog: DialogInterface) {
        super.onCancel(dialog)
        //CANCELを押されたときの処理
    }

    private inner class DialogDateButtonClickLister : DatePickerDialog.OnDateSetListener {
        override fun onDateSet(p0: DatePicker, year: Int, month: Int, day: Int) {
            //OKを押されたときの処理
        }
    }
}

カレンダーダイアログを呼び出すときは以下の処理を記述します。

    fun onClickLimitDate(view :View)
    {
        val dialog = TodoDatePickerDialogFragment()
        dialog.show(supportFragmentManager, "TodoDatePickerDialogFragment")
    }

第1引数はFragmentManagerオブジェクトですこれは呼び出すActivityのsupportFragmentプロパティを指定すればいいです。

第2引数は識別するための文字列なので任意の文字列を設定すればいいです。ここではクラス名をそのまま入れています。

まとめ

日付と時刻のダイアログを表示することができました。手入力で入力した場合、「2020/99/99」なんて打ち方もできてしまうのでダイアログで正しい日付しか入力できないようにしておく方が便利だと思いました。

勉強して開発して動作確認して記事にしてを行っています。
1記事書くのに結構時間が掛かってしまいます。何かいい方法はないものか・・・。

ここまで読んで頂いてありがとうございます。