0
1

JetpackComposeで日時を設定する

Last updated at Posted at 2023-11-29

(育休にてAndroidを久しぶりに触ったので、JetpackComposeを改めて勉強中)

概要

JetpackCompose Material3にて日時設定をしようと思ったら、いまだにDatePickerとTimePickerは別々になっていて面倒だったので、両方を呼び出してDateを取得できるようにする。
(2023/11/27時点だとTimePickerDialogも開発中でサンプルコードがあるのみ)

実装

日付を設定する

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerDialogComponent(
    showPicker: MutableState<Boolean>,
    onCanceled: () -> Unit,
    onSelected: (dateMillis: Long) -> Unit
) {
    if (!showPicker.value) {
        return
    }

    val datePickerState = rememberDatePickerState(
        initialSelectedDateMillis = Date().time
    )

    DatePickerDialog(
        onDismissRequest = {
            showPicker.value = false
            onCanceled()
        },
        confirmButton = {
            TextButton(
                onClick = {
                    val selectedDateMillis = datePickerState.selectedDateMillis
                    datePickerState.setSelection(selectedDateMillis)
                    showPicker.value = false
                    if (selectedDateMillis != null) {
                        onSelected(selectedDateMillis)
                    } else {
                        onCanceled()
                    }
                }
            ) {
                Text(text = "OK")
            }
        },
        dismissButton = {
            TextButton(
                onClick = {
                    showPicker.value = false
                    onCanceled()
                }
            ) {
                Text(text = "CANCEL")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}

時刻を設定する

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerDialogComponent(
    showPicker: MutableState<Boolean>,
    onCanceled: () -> Unit,
    onSelected: (hour: Int, minute: Int) -> Unit
) {
    if (!showPicker.value) {
        return
    }

    val cal = Calendar.getInstance()
    val hour = cal.get(Calendar.HOUR_OF_DAY)
    val minute = cal.get(Calendar.MINUTE)
    val timePickerState = rememberTimePickerState(
        initialHour = hour,
        initialMinute = minute,
        is24Hour = true
    )

    TimePickerDialog(
        onCancel = {
            showPicker.value = false
            onCanceled()
        },
        onConfirm = {
            showPicker.value = false
            onSelected(timePickerState.hour, timePickerState.minute)
        }
    ) {
        TimePicker(state = timePickerState)
    }
}

日付と時刻を両方を交互に呼び出す

@Composable
fun DateTimePickerComponent(
    showPicker: MutableState<Boolean>,
    onSelected: (date: Date) -> Unit
) {

    if (!showPicker.value) {
        return
    }

    val showDatePicker = remember { mutableStateOf(true) }
    val showTimePicker = remember { mutableStateOf(false) }
    val dateMillisState = remember { mutableStateOf<Long?>(null) }

    DatePickerDialogComponent(
        showPicker = showDatePicker,
        onCanceled = {
            showPicker.value = false
        }
    ) { dateMillis ->
        dateMillisState.value = dateMillis
        showTimePicker.value = true
    }

    TimePickerDialogComponent(
        showPicker = showTimePicker,
        onCanceled = {
            showPicker.value = false
        }
    ) { hour, minute ->
        val dateMillis = dateMillisState.value
        if (dateMillis != null) {
            val cal = Calendar.getInstance()
            cal.timeInMillis = dateMillis
            cal.set(Calendar.HOUR_OF_DAY, hour)
            cal.set(Calendar.MINUTE, minute)
            onSelected(cal.time)
        }
        showPicker.value = false
    }
}

使う側

val showPicker = remember { mutableStateOf(false) }

Button(
    modifier = onClick = {
        showPicker.value = true
    }) {
    Text(text = "日時指定で記録")
}

DateTimePickerComponent(showPicker = showPicker) {
    viewModel.save(it)
}

ひとまずはこれで動きました。

もしかしたらDateではなくInstantなどの新しいAPIを使った方が良さそうですかね。
ただJetpackComposeでのダイアログの操作って、昔のAndroidに慣れてると違和感というか面倒だと思ってしまいます…

追記

DatePickerが以下の初期値だと-9時間されてしまってるのに気づきました。。。

initialSelectedDateMillis = Date().time

ドキュメント見るとUTCで作らないといけなさそうですね

initialSelectedDateMillis - timestamp in UTC milliseconds from the epoch that represents an initial selection of a date. Provide a null to indicate no selection. Note that the state's selectedDateMillis will provide a timestamp that represents the start of the day, which may be different than the provided initialSelectedDateMillis.

こちらで作り直したらイケました。

initialSelectedDateMillis = LocalDate.now()
    .atStartOfDay()
    .toInstant(ZoneOffset.UTC)
    .toEpochMilli()

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1