目的
Android の DatePickerDialog を使って、日付範囲(開始日~終了日)を選択させる。
- DatePickerDialogで日付を選んで、Buttonのtextに表示させる。
- Buttonじゃなくて他のViewに表示させてもOK。TextViewとか。
- 選択できる日付を制限する。
- 開始日に終了日より後の日付を選択させない。
- 終了日に開始日より前の日付を選択させない。
その他の仕様(詳しい説明はこちらの記事に書いてます)
すでに日付が選択されており、DatePickerDialogで日付を選ばなかったとき、選択された日付を削除する。
- キャンセルを押したときにButton.textにセットされていた値を変更するということ。
- ここでは日付が未入力の場合、"指定なし"というテキストをセットすることにします。
動作環境
- Kotlin 1.3.21
- Android 8.0 (Oreo)
- AndroidStudio 3.3.2
動作イメージ
実装
layoutファイル
開始日付と終了日付を選択・表示するButtonとTextViewの構成とします。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".MainActivity">
<TextView
android:text="日付範囲"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/date_range_text"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="16dp"/>
<Button
android:text="指定なし"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/begin_date_button"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/date_range_text" android:layout_marginLeft="64dp"
android:layout_marginStart="64dp" android:layout_marginTop="16dp"/>
<TextView
android:text="~"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/range"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/begin_date_button" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" android:layout_marginTop="16dp"/>
<Button
android:text="指定なし"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/end_date_button"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/range" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" android:layout_marginTop="16dp"/>
</android.support.constraint.ConstraintLayout>
activityファイル
以下は、MainActivity.onCreateメソッド内のコードですが、分かりやすくするために2つに分けます。
- dateSetOnClickListener.kt : ButtonのViewを取得とButton.setOnClickListenerの実装を行う。
- DateDialogFragment.kt : DateDialogFragmentを表示させるclass。
dateSetOnClickListener.kt
val mBeginDate = findViewById<Button>(R.id.begin_date_button)
val mEndDate = findViewById<Button>(R.id.end_date_button)
/* 開始日付を指定するボタンを押したとき */
mBeginDate.setOnClickListener {
// DialogFragmentを生成し、表示
DateDialogFragment(mBeginDate).show(supportFragmentManager, mBeginDate::class.java.simpleName)
}
/* 終了日付を指定するボタンを押したとき */
mEndDate.setOnClickListener {
DateDialogFragment(mEndDate).show(supportFragmentManager, mEndDate::class.java.simpleName)
}
DateDialogFragment.kt
/* 日付を入力する時に使用するinnerクラス
DialogFragmentで日付を入力できるカレンダーを表示する
DialogFragmentを呼び出したボタンを取得したいので、コンストラクタの引数にButtonを定義する */
class DateDialogFragment(val button: Button) : DialogFragment() {
/* DatePickerDialogを返却するメソッド
このメソッドで、日付を選択した後の処理や、日付範囲、Dialogのタイトルを設定する */
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// 現在日付を取得
val calendar = Calendar.getInstance()
return DatePickerDialog(
context,
theme,
// DialogFragmentで日付を選択し、OKを押したときの処理
DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
val inputDate = Calendar.getInstance()
// 選択された日付を取得
inputDate.set(year, month, dayOfMonth)
val dfInputeDate = SimpleDateFormat("yyyy-MM-dd", Locale.US)
// CalendarからStringへ変換
val strInputDate = dfInputeDate.format(inputDate.time)
// DialogFragmentを呼び出したボタンのテキストに日付をセット
button.text = strInputDate
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH))
.also {
/* 選択可能な日付の上限を設定
終了日付が指定されている場合、開始日付は終了日付より後を選べないようにする */
if (button === mBeginDate && mEndDate.text != "指定なし"){
val maxDate = Calendar.getInstance()
val dfMaxDate = SimpleDateFormat("yyyy-MM-dd", Locale.JAPAN)
// 指定されている終了日付を取得
val endInputDate = dfMaxDate.parse(mEndDate.text.toString())
maxDate.time = endInputDate
// 選択可能な開始日付の上限に指定されている終了日付を設定
it.datePicker.maxDate = maxDate.timeInMillis
} else {
// 選択可能な開始日付の上限に現在日付を設定
it.datePicker.maxDate = calendar.timeInMillis
}
/* 選択可能な日付の下限を指定 */
val minDate = Calendar.getInstance()
/* 開始日付が指定されている場合、終了日付は開始日付より前を選べないようにする */
if (button === mEndDate && mBeginDate.text != "指定なし"){
val dfMinDate = SimpleDateFormat("yyyy-MM-dd", Locale.JAPAN)
val beginInputDate = dfMinDate.parse(mBeginDate.text.toString())
minDate.time = beginInputDate
// 選択可能な終了日付の下限に指定されている開始日付を設定
it.datePicker.minDate = minDate.timeInMillis
} else {
// 選択可能な終了日付の下限を設定、ここでは2018/2/1とします
minDate.set(2018, 2, 1)
it.datePicker.minDate = minDate.timeInMillis
}
// タイトルが勝手に表示されるのを防ぐために空文字をセット
it.setTitle("")
}
}
/* DialogFragmentでcancelを押した際の処理 */
override fun onCancel(dialog: DialogInterface?) {
super.onCancel(dialog)
// cancelを押した際はセットされていた日付を削除し、"指定なし"をセット
button.text = "指定なし"
}
}