Compose 可组合项 - 日期选择 DatePicker、DatePickerDialog

一、概念

一般是以对话框的形式呼出,DatePickerDialog 就是对 DatePicker 的一个简单对话框封装。

@Composable
fun DatePicker(
    state: DatePickerState,
    modifier: Modifier = Modifier,
    dateFormatter: DatePickerFormatter = remember { DatePickerFormatter() },
    dateValidator: (Long) -> Boolean = { true },
    title: (@Composable () -> Unit)? = {
        DatePickerDefaults.DatePickerTitle(
            state,
            modifier = Modifier.padding(DatePickerTitlePadding)
        )
    },
    headline: (@Composable () -> Unit)? = {
        DatePickerDefaults.DatePickerHeadline(
            state,
            dateFormatter,
            modifier = Modifier.padding(DatePickerHeadlinePadding)
        )
    },
    showModeToggle: Boolean = true,
    colors: DatePickerColors = DatePickerDefaults.colors(

        todayDateBorderColor = Color.Red        //默认选中的当天日期的边框色

        selectedDayContentColor = Color.Red        //选中的文字颜色

        selectedDayContainerColor  = Color.Red        //选中的填充颜色

    )
)

@Composable
fun DatePickerDialog(
    onDismissRequest: () -> Unit,        //关闭对话框回调
    confirmButton: @Composable () -> Unit,        //确认按钮
    modifier: Modifier = Modifier,
    dismissButton: @Composable (() -> Unit)? = null,        //取消按钮
    shape: Shape = DatePickerDefaults.shape,
    tonalElevation: Dp = DatePickerDefaults.TonalElevation,
    colors: DatePickerColors = DatePickerDefaults.colors(),
    properties: DialogProperties = DialogProperties(usePlatformDefaultWidth = false),        //对话框配置,详见Dialog
    content: @Composable ColumnScope.() -> Unit        //传入一个DatePicker
)

输入模式选择模式

二、基本使用

2.1 获取状态

2.1.1 单个日期选择 rememberDatePickerState()

@Composable
fun rememberDatePickerState(
    @Suppress("AutoBoxing") initialSelectedDateMillis: Long? = null,        //默认选中的日期
    @Suppress("AutoBoxing") initialDisplayedMonthMillis: Long? = initialSelectedDateMillis,        //默认显示的月份
    yearRange: IntRange = DatePickerDefaults.YearRange,        //限制选择的年份范围,如 2000..2100
    initialDisplayMode: DisplayMode = DisplayMode.Picker        //选择模式Picker、输入模式Input
): DatePickerState 

2.1.2 范围日期选择 rememberDateRangePickerState()

@Composable
fun rememberDateRangePickerState(
    @Suppress("AutoBoxing") initialSelectedStartDateMillis: Long? = null,        //起始日期
    @Suppress("AutoBoxing") initialSelectedEndDateMillis: Long? = null,        //结束日期
    @Suppress("AutoBoxing") initialDisplayedMonthMillis: Long? =
        initialSelectedStartDateMillis,        //默认显示的月份(如果起始日期没指定就默认是当月)
    yearRange: IntRange = DatePickerDefaults.YearRange,        //允许选择的年份,如 2000..2100
    initialDisplayMode: DisplayMode = DisplayMode.Picker        //选择模式Picker、输入模式Input
): DateRangePickerState

2.1.3 自定义可选择日期 selectableDates

Compose Material3 1.2.0-alpha02 及其以上版本,提供了一个 selectableDates 参数,可以在其中完全自定义可以选择的日期。

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
            }
        }
    )

2.2 DatePicker

val datePickerState = rememberDatePickerState()
DatePicker(state = datePickerState)
Text("当前选中日期的时间戳 ${datePickerState.selectedDateMillis ?: "没有选择"}")

2.3 DatePickerDialog

var isOpenDialog by remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState()
val sdf = remember { SimpleDateFormat("yyy-MM-dd") }

if (isOpenDialog) {
    DatePickerDialog(
        onDismissRequest = { isOpenDialog = false},
        colors = DatePickerDefaults.colors(containerColor = AppColors.White),   //背景色(这个在下面的DatePicker中设置无效)
        confirmButton = {
            Text(
                modifier = Modifier
                    .clickableNoRipple {
                        //拿到的是时间戳,需要格式化
                        val date = datePickerState.selectedDateMillis
                        if (date != null) {
                            onValueChange(sdf.format(date))
                            isDefaultValue = false
                        }
                        isOpenDialog = false
                    },
                text = "确定",
            )
        },
        dismissButton = {
            Text(
                modifier = Modifier.clickableNoRipple { isOpenDialog = false },
                text = "取消"
            )
        }
    ) {
        DatePicker(
            state = datePickerState,
            colors = DatePickerDefaults.colors(
                todayDateBorderColor = AppColors.Primary,   //默认选中的当天日期的边框色
                selectedDayContentColor = AppColors.White,  //选中的文字颜色
                selectedDayContainerColor = AppColors.Primary,  //选中的填充颜色
            )
        )
    }
}

三、封装

3.1 单按钮时间选择对话框

@Preview
@Composable
private fun SingleButtonDatePickerPreView() {
    var value by remember { mutableStateOf("选择时间") }
        SingleButtonDatePickerView(
            value = value,
            onValueChange = { value = it },
        )
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SingleButtonDatePickerView(
    modifier: Modifier = Modifier,
    value: String = "选择时间",
    onValueChange: (String) -> Unit,
    borderColor: Color = AppColors.Hint,
) {
    var isOpenDialog by remember { mutableStateOf(false) }
    var isDefaultValue by remember { mutableStateOf(true) }
    val datePickerState = rememberDatePickerState()
    val sdf = remember { SimpleDateFormat("yyy-MM-dd") }
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .border(
                width = 1.dp,
                color = borderColor,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = AppDimens.paddingContent)
            .clickableNoRipple { isOpenDialog = true },
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = value,
            maxLines = 1,
            color = if(isDefaultValue) AppColors.Hint else AppColors.Black,
            fontSize = AppDimens.textPrimary,
        )
        Icon(
            modifier = Modifier.padding(start = 5.dp),
            imageVector = Icons.Default.CalendarMonth,
            contentDescription = null,
            tint = AppColors.Hint
        )
    }
    if (isOpenDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),   //背景色(这个在下面的DatePicker中设置无效)
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = datePickerState.selectedDateMillis
                            if (date != null) {
                                onValueChange(sdf.format(date))
                                isDefaultValue = false
                            } else {
                                isDefaultValue = true
                            }
                            isOpenDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = datePickerState,
                colors = DatePickerDefaults.colors(
                    todayDateBorderColor = AppColors.Primary,   //默认选中的当天日期的边框色
                    selectedDayContentColor = AppColors.White,  //选中的文字颜色
                    selectedDayContainerColor = AppColors.Primary,  //选中的填充颜色
                )
            )
        }
    }
}

3.2 双按钮时间选择对话框

@Preview
@Composable
private fun TwoButtonDatePickerPreview() {
    var startDate by remember { mutableStateOf("开始时间") }
    var endDate by remember { mutableStateOf("结束时间") }
        TwoButtonDatePickerView(
            startDate = startDate,
            endDate = endDate,
            onStartChange = { startDate = it },
            onEndChange = { endDate = it },
        )
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TwoButtonDatePickerView(
    modifier: Modifier = Modifier,
    startDate: String = "开始时间",
    endDate: String = "结束时间",
    onStartChange: (String) -> Unit,
    onEndChange: (String) -> Unit,
    borderColor: Color = AppColors.Hint,
) {
    var isOpenStartDialog by remember { mutableStateOf(false) }
    var isOpenEndDialog by remember { mutableStateOf(false) }
    val startDatePickerState = rememberDatePickerState()
    val endDatePickerState = rememberDatePickerState()
    var isStartDateDefault by remember { mutableStateOf(true) }
    var isEndDateDefault by remember { mutableStateOf(true) }
    val sdf = remember { SimpleDateFormat("yyy-MM-dd") }
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .border(
                width = 1.dp,
                color = borderColor,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = AppDimens.paddingContent),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(
            modifier = Modifier.clickableNoRipple { isOpenStartDialog = true },
            text = startDate,
            color = if (isStartDateDefault) AppColors.Hint else AppColors.Black,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
        Icon(
            modifier = Modifier
                .size(25.dp)
                .padding(horizontal = 5.dp),
            painter = painterResource(id = R.drawable.icon_arrow_right),
            contentDescription = null,
            tint = AppColors.Hint
        )
        Text(
            modifier = Modifier.clickableNoRipple { isOpenEndDialog = true },
            text = endDate,
            color = if (isEndDateDefault) AppColors.Hint else AppColors.Black,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
        Icon(
            modifier = Modifier.padding(start = 5.dp),
            imageVector = Icons.Default.CalendarMonth,
            contentDescription = null,
            tint = AppColors.Hint
        )
    }
    if (isOpenStartDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenStartDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = startDatePickerState.selectedDateMillis
                            if (date != null) {
                                onStartChange(sdf.format(date))
                                isStartDateDefault = false
                            } else {
                                isStartDateDefault = true
                            }
                            isOpenStartDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenStartDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = startDatePickerState,
                colors = DatePickerDefaults.colors(
                    todayDateBorderColor = AppColors.Primary,
                    selectedDayContentColor = AppColors.White,
                    selectedDayContainerColor = AppColors.Primary,
                )
            )
        }
    }
    if (isOpenEndDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenEndDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = endDatePickerState.selectedDateMillis
                            if (date != null) {
                                onEndChange(sdf.format(date))
                                isEndDateDefault = false
                            } else {
                                isEndDateDefault = true
                            }
                            isOpenEndDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenEndDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = endDatePickerState,
                colors = DatePickerDefaults.colors(
                    containerColor = AppColors.White,
                    todayDateBorderColor = AppColors.Primary,
                    selectedDayContentColor = AppColors.White,
                    selectedDayContainerColor = AppColors.Primary,
                )
            )
        }
    }
}
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值