前语

在之前我运用 Comose 写 APP 的时候,官方还没有给出关于 DatePicker 的解决计划。

其时为了在 Compose 中完成 DatePicker ,大致有两种计划:

一是运用原生 VIew 的 DatePicker,可是因为觉得我即然都用 Compose 了,再去用 VIew ,总觉得怪怪的,所以就没有用这个计划。

二是运用他人写的第三方 DatePicker,我其时采用的就是这个计划。

可是找了一圈,只找到一个相对好用的库,然而这个库是个法国人写,所以对中文的支撑不是太好,至于这个不是太好,是什么意思呢?你们看图就知道了:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

哈哈哈,星期的缩写都是 “星”。

关于这个问题,我也提了 ISSUE,并且具体解释了问题来历以及解决方法,可是作者并没有理我,直至今日都没有修正这个问题。

至于我为什么不自己修正之后提 PR,看其间一个回复:

I’m thinking about ability to inject the functionality from outside if necessary. Default function would be getDisplayName() but it can be overriden by the code similar to the one here. It’s obviously a bug in the Android implementation, so it shouldn’t be fixed by this library.

所以这个问题就这么放置了。

直到最近,我翻阅 Compose 更新日志时,发现从 Compose Material3 1.1.0 版别开端,新增了 DatePicker DateRangePicker DatePickerDialog 三个组件。

终于,官方出日期挑选了,这不得来学学。

根本用法

首要,是最根本的 DatePicker 的运用。

DatePicker 只有一个有必要参数 state,用于设置一些配置信息以及获取当时选中的日期。

咱们能够经过 rememberDatePickerState 生成 DatePicker 需求的 state

Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
    val datePickerState = rememberDatePickerState()
    DatePicker(state = datePickerState, modifier = Modifier.padding(16.dp))
    Text("当时选中日期的时刻戳 ${datePickerState.selectedDateMillis ?: "没有挑选"}")
}

作用如下:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

在这个挑选页面中,支撑经过点击日期周围的编辑图标切换至手动输入形式:

Jetpack Compose Material3 组件之 DatePicker(日期选择)
当然,咱们也能够经过设置 rememberDatePickerState 的参数来指定初始化显现日期挑选界面还是输入框界面:

val datePickerState = rememberDatePickerState(
    initialDisplayMode = DisplayMode.Picker // 默许显现挑选框
    // initialDisplayMode = DisplayMode.Input // 默许显现输入框
)

另外,咱们也能够设置默许展现的月份和约束只能挑选的年份:

val datePickerState = rememberDatePickerState(
    yearRange = 2023..2024,
    initialDisplayedMonthMillis = 1685577600000 // 注意这里是时刻戳
)

假如想要愈加自在的约束能够挑选的日期,则需求运用 Compose Material3 1.2.0-alpha02 及其以上版别。

在这个版别中供给了一个叫 selectableDates 的参数,能够在其间彻底自定义能够挑选的日期,这里以官方的 sample 举例,假如咱们想约束制止挑选周末,且只能挑选2023年以后的日期,那么能够这样写:

val datePickerState = rememberDatePickerState(
        selectableDates = object : SelectableDates {
            // 制止挑选周末(周六和周日)
            override fun isSelectableDate(utcTimeMillis: Long): Boolean {
                return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    val dayOfWeek = Instant.ofEpochMilli(utcTimeMillis).atZone(ZoneId.of("UTC"))
                        .toLocalDate().dayOfWeek
                    dayOfWeek != DayOfWeek.SUNDAY && dayOfWeek != DayOfWeek.SATURDAY
                } else {
                    val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
                    calendar.timeInMillis = utcTimeMillis
                    calendar[Calendar.DAY_OF_WEEK] != Calendar.SUNDAY &&
                            calendar[Calendar.DAY_OF_WEEK] != Calendar.SATURDAY
                }
            }
            // 只答应挑选2023年曾经
            override fun isSelectableYear(year: Int): Boolean {
                return year > 2022
            }
        }
    )

运转作用如下:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

能够看到周末是灰色的,不行选中。

点开挑选年份时,2023 年曾经不行挑选:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

在对话框中运用

上面一节讲的仅仅根本的运用,可是实践开发过程中,或许还是在 Dialog 中挑选日期的场景更多。

所以官方也供给了一个 DatePickerDialog 组件。

其实看 DatePickerDialog 的源码就能看出,它也仅仅简略封装了一下 AlertDialog:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

所以实践上运用和 DatePicker 根本没有差异,仅仅需求额外处理 dialog 的状况,这里依旧以官方 sample 为例:

val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
    val datePickerState = rememberDatePickerState()
    val confirmEnabled = derivedStateOf { datePickerState.selectedDateMillis != null }
    DatePickerDialog(
        onDismissRequest = {
            openDialog.value = false
        },
        confirmButton = {
            TextButton(
                onClick = {
                    openDialog.value = false
                    println("选中时刻戳为: ${datePickerState.selectedDateMillis}")
                },
                enabled = confirmEnabled.value
            ) {
                Text("确认")
            }
        },
        dismissButton = {
            TextButton(
                onClick = {
                    openDialog.value = false
                }
            ) {
                Text("取消")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}

运转作用如下:

Jetpack Compose Material3 组件之 DatePicker(日期选择)

日期范围挑选

除此之外,在 MD3 新的 API 中还供给了一个能够挑选日期范围的函数 DateRangePicker

它的参数与 DatePicker 相似,仅仅 state 变为了 DateRangePickerState

咱们能够经过 rememberDateRangePickerState 生成一个 state

state 中,咱们能够设置时刻挑选器的初始化展现形式(initialDisplayMode)、默许起始日期(initialSelectedStartDateMillis)、默许结束日期(initialSelectedEndDateMillis)、默许展现日期(initialDisplayedMonthMillis)、答应挑选的年份(yearRange)。

并且,相同的,在 Compose Material3 1.2.0-alpha02 及其以上版别还支撑彻底自定义能够挑选的日期 selectableDates

该函数的显现作用如下:

val state = rememberDateRangePickerState()
DateRangePicker(state = state, modifier = Modifier.fillMaxSize())

Jetpack Compose Material3 组件之 DatePicker(日期选择)

获取选中的值依旧是经过 sate:

println("挑选的时刻戳范围: ${state.selectedStartDateMillis}..${state.selectedEndDateMillis}")

总结

本文仅仅简要介绍了关于 Compsoe Material3 中关于日期挑选的根本运用方法,更多的运用方法还需求读者自行探索。

能够看到,Compose 的官方组件现已越来越多,越来越趋向于成熟。

相较于正式版刚发布没多久时的什么东西都没有,什么都需求自己造轮子的状况,现在简直现已涵盖了咱们开发中常用到的各种控件和需求了。