Android の DatePickerDialog で選択許可する年月日を制限すると不具合に遭遇したでござるの巻

# 目的

Android の [DatePickerDialog](https://developer.android.com/reference/android/app/DatePickerDialog.html) でユーザーが自身の誕生日を選択するシチュエーションにおいては、生前や未来など不正な値を設定できないアーキテクチャにしておきたいです。

# 実践

Stack Overflow の[回答](https://stackoverflow.com/a/18353944)を参考に、本日（2018年1月13日）より先の年月日を選択できないようにします。

# 不具合 :one: 

Issue Tracker にも[起票されています](https://issuetracker.google.com/issues/37085752)が Android 6.0 (Marshmallow) 端末では、年月日の選択範囲に上限または下限の制約を加えると、ダイアログのタイトルが勝手に表示されるバグがあります :sob:

<img src="https://qiita-image-store.s3.amazonaws.com/0/6881/e77a7d9e-e75a-e493-e736-e020183144f5.png" width="350px" />

## 解決策

ダイアログのタイトルに空文字をセットすれば非表示にできると Stack Overflow に[回答](https://stackoverflow.com/a/33486666)がありました :ok_woman:

# 不具合 :two:

Android 5.0 (Lollipop) 端末では、許容範囲外の日付をタップで選択できるというバグがあります :sob:
Issue Tracker の[情報](https://issuetracker.google.com/issues/37018603)によると Android 5.1 では修正されているようです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/6881/31958e74-0f22-4cc8-c16c-91937ca37ab2.png" width="350px" />

## 解決策

OS バージョンが 5.0 の場合は、ダイアログに Holo テーマをセットして [NumberPicker](https://developer.android.com/reference/android/widget/NumberPicker.html) による選択方式にしてしまう案を思い付きました :bulb:（デザインの統一が阻まれそうですが）。
こちらは制限した範囲の年月日が選択不可になったので（ソフトキーからの数値入力でも）バグは回避できそうです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/6881/f02d1da6-9bc6-7e96-29b4-9e0f97699aaa.png" width="350px" />

# ある程度のバグを塞いだであろう実装

[Kotlin](https://kotlinlang.org/) で書いてみました :writing_hand:

``` DatePickerDialogFragment.kt
import android.app.AlertDialog
import android.app.DatePickerDialog
import android.app.DatePickerDialog.OnDateSetListener
import android.app.Dialog
import android.os.Build
import android.os.Bundle
import android.support.v4.app.DialogFragment
import java.util.*

/**
 * 年月日を選択するダイアログ
 */
class DatePickerDialogFragment : DialogFragment() {

    companion object {
        fun newInstance(): DatePickerDialogFragment = DatePickerDialogFragment()
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val calendar = Calendar.getInstance()
        return DatePickerDialog(
                context,
                // Lollipop の場合は Holo テーマのダイアログにする
                if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) AlertDialog.THEME_HOLO_LIGHT else theme,
                OnDateSetListener { view, year, month, dayOfMonth -> },
                calendar.get(Calendar.YEAR),
                calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH))
                .also {
                    // 選択可能な年月日の上限を指定します
                    it.datePicker.maxDate = calendar.timeInMillis
                    // タイトルが勝手に表示されるのを防ぐために空文字をセットします
                    it.setTitle("")
                }
    }
}
```

``` MainActivity.kt
import android.os.Bundle
import android.support.v7.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        DatePickerDialogFragment
                .newInstance()
                .show(supportFragmentManager, DatePickerDialogFragment::class.java.simpleName)
    }
}
```

## 実行結果

Android 8.0 (Oreo) のエミュレータで表示しました :calendar:

<img src="https://qiita-image-store.s3.amazonaws.com/0/6881/f769463f-6c3a-e4db-f5e2-9548f99055ff.png" width="350px" />