有没有更简洁的方式获取DatePickerDialog的选中值?
用协程实现可复用的日期/时间选择器挂起函数
核心思路是利用suspendCancellableCoroutine将回调式的Dialog转为挂起函数,同时确保Dialog的创建、显示全流程在UI线程执行,解决回调繁琐的问题,同时避免线程异常。
1. 日期选择器挂起函数实现
在Fragment中编写如下可复用代码:
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import java.time.LocalDateTime suspend fun showDatePickerDlg(yr: Int, mth: Int, day: Int): LocalDateTime { // 强制所有UI操作在主线程执行 return withContext(Dispatchers.Main) { suspendCancellableCoroutine { cont -> val datePickerDialog = DatePickerDialog(requireActivity()) { _, year, monthOfYear, dayOfMonth -> // 用户选择日期后恢复协程,返回选中值(注意DatePicker月份是0-based,转成1-based后传入LocalDateTime) cont.resume(LocalDateTime.of(year, monthOfYear + 1, dayOfMonth, 0, 0), null) } // 设置初始日期,若传入的mth是1-based,这里直接传mth-1适配DatePicker的0-based规则 datePickerDialog.datePicker.init(yr, mth, day, null) // 处理用户取消对话框的场景,避免协程长期挂起 datePickerDialog.setOnCancelListener { cont.cancel() } datePickerDialog.show() } } }
2. 时间选择器挂起函数实现
同理,时间选择器的可复用实现如下:
import java.time.LocalDateTime import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext suspend fun showTimePickerDlg(hour: Int, minute: Int): LocalDateTime { return withContext(Dispatchers.Main) { suspendCancellableCoroutine { cont -> val timePickerDialog = TimePickerDialog(requireActivity()) { _, selectedHour, selectedMinute -> cont.resume(LocalDateTime.of(0, 1, 1, selectedHour, selectedMinute), null) } timePickerDialog.timePicker.setInitialHour(hour) timePickerDialog.timePicker.setInitialMinute(minute) timePickerDialog.setOnCancelListener { cont.cancel() } timePickerDialog.show() } } }
3. 正确调用方式
禁止使用GlobalScope,它不与Fragment生命周期绑定,容易引发内存泄漏。应使用Fragment自带的lifecycleScope:
// 示例:在按钮点击事件中调用 btnPickDateTime.setOnClickListener { lifecycleScope.launch { try { // 注意:若初始月份是1-based,这里传入mth-1(比如5月传4)适配DatePicker规则 val schedDate = showDatePickerDlg(2024, 4, 20) val schedTime = showTimePickerDlg(14, 30) // 合并日期与时间 val fullDateTime = schedDate.withHour(schedTime.hour).withMinute(schedTime.minute) Log.i("TAG", "选中的完整时间: $fullDateTime") } catch (e: CancellationException) { // 用户取消选择时的逻辑处理 Log.i("TAG", "用户取消了日期/时间选择") } } }
你的代码为何无法显示对话框?
- 线程调度错误:
GlobalScope.async默认在后台线程执行挂起函数,即使嵌套runOnUiThread显示Dialog,Dialog的创建过程仍在后台线程,违反Android UI操作规范,导致初始化异常。 - 协程作用域不安全:
GlobalScope不受Fragment生命周期管理,可能在Fragment销毁后才执行,或调度优先级过低导致Dialog无法正常显示。 - 月份参数适配错误:
DatePickerDialog的月份参数是0-based,若你传入1-based的月份值,会导致初始日期异常,甚至Dialog显示异常。
内容的提问来源于stack exchange,提问作者rminaj
相关产品推荐
相关产品推荐

