【Android】初心者向け シンプルなドラムロール式DatePickerサンプル

Android開発初心者の私はシンプルなドラムロール式のDatePickerを実装しようとした時に、意外と複数のサイトを参照しながら試行錯誤しました。
同じような初心者の方の参考になれば幸いです。

#ドラムロール式DatePickerのイメージ
![Screenshot_1641257836.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/757ecb2d-7383-3a2a-5573-652db16b7f8b.png)

![2a1aee80cd619115f682e1a0b162a90d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/b057ae8f-7a9a-6128-4f5f-02cdeed9c911.gif)


#サンプルの仕様
・日付入力欄は直接日付を入力することもできる。
・入力欄が空白など日付として認識できない場合、初期値として今日がDatePickerに表示される。
・入力欄が日付の場合、DatePickerには入力欄の日付が初期表示される。
・DatePickerで選択した日付が入力欄に反映される。

#サンプルの仕様変更（2022年1月6日）
:::note alert
■ 古い仕様
・日付入力欄をクリックするとドラムロール式DatePickerが表示される。
:::

:::note info
■ 変更後の仕様 
※  日付入力欄のどこをクリックしてもDatePickerを表示する仕様だとうざったいので仕様変更
・日付入力欄はクリックしてもなにも起こらない。
・下向き矢印を入力欄とは独立したImageViewにし、下向き矢印をクリックした時だけDatePickerを表示する。
:::

#環境
android studio arctic fox | 2020.3.1 patch 4
compileSdk 32

#コード
さっそくコードの主要部分を紹介します。
ここではわかり易さを重視して必要な部分のコードのみ抜粋しています。
ソース全体は https://github.com/masayahak/Datepicker へアップしています。

# DatePickerを呼び出すアクティビティ
![Screenshot_1641260151.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/7f235828-2087-b213-b59b-248f0eb4784d.png)

```xml:activity_main.xml
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/date_label"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="販売開始日"
        app:startIconDrawable="@drawable/ic_calendar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/date"
            android:layout_width="190dp"
            android:layout_height="60dp"
            android:inputType="date"
            android:clickable="false"
            />
    </com.google.android.material.textfield.TextInputLayout>

    <!-- DatePicker表示ボタン -->
    <ImageButton
        android:id="@+id/date_picker_actions"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:contentDescription="DatePicker"
        android:src="@drawable/ic_arrow_down"
        app:layout_constraintTop_toTopOf="@id/date_label"
        app:layout_constraintBottom_toBottomOf="@id/date_label"
        android:layout_marginTop="4dp"
        app:layout_constraintEnd_toEndOf="@id/date_label"
        android:layout_marginEnd="8dp"
        app:tint="@android:color/black"
        android:background="@color/white"
        />
```
日付入力欄としてTextInputEditTextを利用し、android:clickable="false"にしました。
DatePickerダイアログを表示するのはImageButtonが担当しています。
ImageButtonのandroid:src="@drawable/ic_arrow_down"は下向きの矢印アイコンです。
クリックでDatePickerダイアログが表示されることを明示するためのアイコンです。

```kotlin:MainActivity.kt
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val img = findViewById<ImageView>(R.id.date_picker_actions)
        img.setOnClickListener {
            // ドラムロール式DatePickerを呼び出す
            val eText = findViewById<View>(R.id.date) as EditText
            MyDatePicker.showDatePicker(eText)
        }
    }
```
MyDatePicker.showDatePickerで汎用化したDatePickerダイアログを呼び出しています。
引数へDatePickerが値を受け渡しする日付入力用のEditTextを渡してます。

# 汎用化したDatePicker
```kotlin:MyDatePicker.kt
@RequiresApi(Build.VERSION_CODES.O)
object MyDatePicker {

    fun showDatePicker (eText : EditText) {

        // 日付入力フィールドが空白など日付ではないときの初期値として、「今日」を設定
        val defaultDate = LocalDate.now()
        var year = defaultDate.year
        var monthValue = defaultDate.monthValue
        var dayOfMonth = defaultDate.dayOfMonth

        // 日付入力フィールドの値が日付の場合はその値を設定
        val ldt = toLocaleDate(eText.text.toString())
        if (ldt != null) {
            year = ldt.year
            monthValue = ldt.monthValue
            dayOfMonth = ldt.dayOfMonth
        }

        // ドラム式DatePicker表示
        val picker = DatePickerDialog(
            eText.rootView.context,
            AlertDialog.THEME_HOLO_LIGHT,   // テーマ：ドラム式 背景白

            // ダイアログでOKをクリックされたときの処理 日付入力フィールドへ値を設定
            { _, getYear, getMonthOfYear, getDayOfMonth
                -> eText.setText(String.format("%d/%02d/%02d",
                getYear,
                getMonthOfYear + 1,     // 月はZEROオリジン
                getDayOfMonth))
            },

            // DatePickerが初期表示する日付
            year,
            monthValue - 1, // 月はZEROオリジン
            dayOfMonth
        )
        picker.show()

    }

    private const val DATE_FORMAT = "yyyy/MM/dd"

    fun toLocaleDate(stringDate : String) : LocalDate? {
        val df = DateTimeFormatter.ofPattern(DATE_FORMAT)
        return try { LocalDate.parse(stringDate, df) } catch (t: Throwable) { null }
    }

}
```

#終わりに
非常に短いコードですが、１つずつ調べながら手作りするのは大変ですよね。お役に立てれば幸いです。
コメント、質問も歓迎です。