[material3] Jetpack ComposeでDate Pickerを表示する

## 背景
Compose Material3の[1.1.0-alpha04](https://developer.android.com/jetpack/androidx/releases/compose-material3?hl=ja#1.1.0-alpha04)で `Date Picker` が追加されました。
これの使い方について、現時点で日本語の記事が見つからなかったので、簡単にまとめます。
なおアルファ版での確認のため、今後変更される可能性が大いにあります。

## 実行環境
``` app/build.gradle
implementation 'androidx.compose.material3:material3:1.1.0-alpha04'
```

## まず表示してみる
``` MyDatePicker.kt
@ExperimentalMaterial3Api
@Composable
fun MyDatePicker() {
    DatePicker(datePickerState = rememberDatePickerState())
}

@ExperimentalMaterial3Api
@Preview(showBackground = true, device = Devices.PIXEL)
@Composable
fun MyDatePickerPreview() {
    MyDatePicker()
}
```
`DatePicker`は現在実験的なComposeのため、`@ExperimentalMaterial3Api`アノテーションを付けています。
上記のコードにより下記プレビューが表示されます。
デフォルトで今月が表示され、本日の日付が丸で囲われるようです。
![スクリーンショット 2023-01-25 22.45.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610553/39e250b7-f401-03a7-b127-e9ac30ae2d87.png)

ここでプレビューをInteractive Modeで実行し、1月27日をタップするとどうなるでしょうか。
![スクリーンショット 2023-01-25 22.44.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610553/6941269a-b536-c561-ae21-b0710744dfe2.png)
タップした日付が、紫で塗り潰された丸で囲われました。
表示される日付の表記が少しおかしいですね。

## DatePickerの実装
ここで、DatePickerの実装を見てみます。
```DatePicker.kt
@ExperimentalMaterial3Api
@Composable
fun DatePicker(
    datePickerState: DatePickerState,
    modifier: Modifier = Modifier,
    dateFormatter: DatePickerFormatter = remember { DatePickerFormatter() },
    dateValidator: (Long) -> Boolean = { true },
    title: (@Composable () -> Unit)? = { DatePickerDefaults.DatePickerTitle() },
    headline: @Composable () -> Unit = {
        DatePickerDefaults.DatePickerHeadline(
            datePickerState,
            dateFormatter
        )
    },
    colors: DatePickerColors = DatePickerDefaults.colors()
)
```
呼び出し側で必須なのは`DatePickerState`だけです。
`rememberDatePickerState()`はDatePickerStateをrememberSaveableで覚えてくれるので、Recomposeや画面回転されても入力中の日付が消えません。また、内部で指定されているYearsRangeは`IntRange(1900, 2100)`だったので、デフォルトでは1900年から2100年が選択可能であるようです。

## 選択された日付の取得
選択された日付は`DatePickerState`にエポックミリ秒で保持されています。
エポックミリ秒だと扱いづらいので、LocalDateに変換すると下記のようになります。
```MyDatePicker.kt
@ExperimentalMaterial3Api
@Composable
fun MyDatePicker() {
    val datePickerState = rememberDatePickerState()
    DatePicker(datePickerState = datePickerState)
    val selectedDate: LocalDate? = datePickerState.selectedDateMillis?.let { Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate() }
}
```
日付が選択されていない時は、エポックミリ秒がnullなので気をつけましょう。

## DatePickerのカスタマイズ
先ほど表示したDatePickerに下記の要件を加えます。
* 2023年2月を初期表示する
* 2023年2月15日をデフォルトで選択する
* 日付を日本語のフォーマットで表示する

``` MyDatePicker.kt
@ExperimentalMaterial3Api
@Composable
private fun MyDatePicker() {
    val initialSelectedDateMillis = LocalDate.of(2023, 2, 15)
        .atStartOfDay()
        .toEpochSecond(ZoneOffset.UTC)
        .toMillis()
    DatePicker(
        datePickerState = rememberDatePickerState(
            initialSelectedDateMillis,
            initialSelectedDateMillis
        ),
        dateFormatter = DatePickerFormatter(
            shortFormat = "M月 d日 (E)", // 今年の日付を選択している時のフォーマット
            mediumFormat = "yyyy年 M月 d日", // 今年以外の年の日付を選択している時のフォーマット
            monthYearFormat = "yyyy年 M月" // 年月選択部分のフォーマット
        )
    )
}

private fun Long.toMillis() = this * 1000

@ExperimentalMaterial3Api
@Preview(showBackground = true, device = Devices.PIXEL)
@Composable
fun MyDatePickerPreview() {
    MyDatePicker()
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610553/3016c045-f881-3151-c64a-c8759805009c.png)
初期表示する日付はLong（エポックミリ秒）で指定します。月も同じなので、初期表示する日付と月が同じであれば、同じ値を指定すればいいようです。
選択された日付のフォーマットも「今年を選択しているかどうか」で2つ指定する必要があります。
また今回は指定していませんが、DatePickerの色も引数で指定することができます。かつての実装（Android View）ではThemeで色指定しなければならず、さらに何の属性でどこの部分の色が変わるか分かりづらかったことを考えると、非常にやりやすくなりました。


### 終わりに
今回はCompose Material3の`DatePicker`を試してみました。
アルファ版のため仕様/実装は変わる可能性がありますが、かつての実装と比べると日付フォーマットや色など変更しやすく、直感的に使用できるいいものだと感じました。
今後安定版になることを楽しみにしています。