当前位置: 首页 > news >正文

Android Studio jetpack compose折叠日历日期选择器【折叠日历】

今天写一个日期选择器,大家根据自己需求改代码,记得点赞支持,谢谢~
这是进入的默认状态
在这里插入图片描述

折叠状态选中本周其他日期状态

在这里插入图片描述

切换上下周状态

在这里插入图片描述

展开日历状态

切换上下月状态

在这里插入图片描述
在这里插入图片描述
选中状态

在这里插入图片描述

代码如下:

import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.fj.test.utils.SetSystemUiColors
import com.fj.test.viewmodel.CollapsibleCalendarViewModel
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.time.format.TextStyle
import java.util.*@Composable
fun CollapsibleCalendarScreen(viewModel: CollapsibleCalendarViewModel) {val currentMonth by viewModel.currentMonth.collectAsState()val isExpanded by viewModel.isExpanded.collectAsState()val selectedDate by viewModel.selectedDate.collectAsState()val context = LocalContext.current// 设置系统 UI 颜色SetSystemUiColors(statusBarColor = Color.Transparent,navigationBarColor = Color.White,useDarkIcons = true)// 主界面Box(modifier = Modifier.fillMaxSize().background(Color(0xFFF5F5F9))) {Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {// 顶部标题栏CalendarTopBar(context, viewModel)Spacer(modifier = Modifier.height(16.dp))// 日历卡片CalendarCard(currentMonth = currentMonth,isExpanded = isExpanded,selectedDate = selectedDate,onPrevMonth = { viewModel.prevMonth() },onNextMonth = { viewModel.nextMonth() },onToggleExpand = { viewModel.toggleExpand() },onSelectDate = { viewModel.selectDate(it) })Spacer(modifier = Modifier.height(16.dp))// 这里可以添加选中日期的详细信息Text(text = "选中日期: ${selectedDate.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))}",style = MaterialTheme.typography.bodyLarge,modifier = Modifier.padding(8.dp))}}
}@Composable
fun CalendarTopBar(context: Context, viewModel: CollapsibleCalendarViewModel) {Box(modifier = Modifier.fillMaxWidth().height(60.dp),) {IconButton(onClick = { /* 返回上一页 */ },modifier = Modifier.align(Alignment.CenterStart)) {Icon(imageVector = Icons.Default.ArrowBack,contentDescription = "返回",tint = Color(0xFF111111))}Text(modifier = Modifier.align(Alignment.Center),text = "折叠日历",fontSize = 16.sp,color = Color(0xFF111111),maxLines = 1,)}
}@Composable
fun CalendarCard(currentMonth: LocalDate,isExpanded: Boolean,selectedDate: LocalDate,onPrevMonth: () -> Unit,onNextMonth: () -> Unit,onToggleExpand: () -> Unit,onSelectDate: (LocalDate) -> Unit
) {Card(modifier = Modifier.fillMaxWidth(),shape = RoundedCornerShape(16.dp),colors = CardDefaults.cardColors(containerColor = Color.White)) {Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {// 月份选择器Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {IconButton(onClick = onPrevMonth) {Icon(imageVector = Icons.Default.ArrowBack,contentDescription = if (isExpanded) "上个月" else "上周")}Text(text = if (isExpanded) currentMonth.format(DateTimeFormatter.ofPattern("yyyy年MM月"))else "${selectedDate.format(DateTimeFormatter.ofPattern("MM月dd日"))} - ${selectedDate.plusDays(6).format(DateTimeFormatter.ofPattern("MM月dd日"))}",modifier = Modifier.weight(1f),textAlign = TextAlign.Center,fontWeight = FontWeight.Bold,fontSize = 18.sp)IconButton(onClick = onNextMonth) {Icon(imageVector = Icons.Default.ArrowForward,contentDescription = if (isExpanded) "下个月" else "下周")}IconButton(onClick = onToggleExpand) {Icon(imageVector = Icons.Default.ArrowDropDown,contentDescription = "展开/收起")}}Spacer(modifier = Modifier.height(8.dp))// 星期标题 - 从周日开始WeekdayHeader()// 日历内容AnimatedVisibility(visible = isExpanded,enter = expandVertically(),exit = shrinkVertically()) {// 月视图MonthCalendar(currentMonth = currentMonth,selectedDate = selectedDate,onSelectDate = onSelectDate)}if (!isExpanded) {// 周视图WeekCalendar(selectedDate = selectedDate,onSelectDate = onSelectDate)}}}
}@Composable
fun WeekdayHeader() {// 重新排序星期,使周日在第一位val daysOfWeek = listOf(DayOfWeek.SUNDAY,DayOfWeek.MONDAY,DayOfWeek.TUESDAY,DayOfWeek.WEDNESDAY,DayOfWeek.THURSDAY,DayOfWeek.FRIDAY,DayOfWeek.SATURDAY)Row(modifier = Modifier.fillMaxWidth()) {for (dayOfWeek in daysOfWeek) {Box(modifier = Modifier.weight(1f).padding(vertical = 8.dp),contentAlignment = Alignment.Center) {Text(text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.CHINA),fontSize = 14.sp,color = if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY)Color(0xFFE57373) else Color(0xFF666666))}}}
}@Composable
fun MonthCalendar(currentMonth: LocalDate,selectedDate: LocalDate,onSelectDate: (LocalDate) -> Unit
) {val yearMonth = YearMonth.of(currentMonth.year, currentMonth.month)val firstDay = yearMonth.atDay(1)val lastDay = yearMonth.atEndOfMonth()// 获取当月第一天是周几(0=周日,1=周一,...,6=周六)val firstDayOfWeek = firstDay.dayOfWeek.value % 7val daysInMonth = yearMonth.lengthOfMonth()val days = remember(currentMonth) {val list = mutableListOf<LocalDate>()val previousMonth = yearMonth.minusMonths(1)val daysInPreviousMonth = previousMonth.lengthOfMonth()// 添加上个月的日期填充第一行for (i in 0 until firstDayOfWeek) {val day = daysInPreviousMonth - firstDayOfWeek + i + 1list.add(LocalDate.of(previousMonth.year, previousMonth.month, day))}// 添加当月的日期for (i in 1..daysInMonth) {list.add(LocalDate.of(currentMonth.year, currentMonth.month, i))}// 计算需要填充的下个月天数val remainingCells = 42 - list.size // 6行7列 = 42个格子// 添加下个月的日期填充剩余行val nextMonth = yearMonth.plusMonths(1)for (i in 1..remainingCells) {list.add(LocalDate.of(nextMonth.year, nextMonth.month, i))}list}LazyVerticalGrid(columns = GridCells.Fixed(7),modifier = Modifier.height(240.dp)) {items(days) { date ->DayItem(date = date,selectedDate = selectedDate,isCurrentMonth = date.month == currentMonth.month,onSelectDate = onSelectDate)}}
}@Composable
fun WeekCalendar(selectedDate: LocalDate,onSelectDate: (LocalDate) -> Unit
) {// 获取当前选中日期所在的周(从周日开始)val startOfWeek = remember(selectedDate) {// 计算周日selectedDate.minusDays((selectedDate.dayOfWeek.value % 7).toLong())}val weekDays = remember(startOfWeek) {(0..6).map { startOfWeek.plusDays(it.toLong()) }}Row(modifier = Modifier.fillMaxWidth()) {weekDays.forEach { date ->Box(modifier = Modifier.weight(1f).padding(4.dp),contentAlignment = Alignment.Center) {DayItem(date = date,selectedDate = selectedDate,isCurrentMonth = true, // 周视图不需要区分当前月onSelectDate = onSelectDate)}}}
}@Composable
fun DayItem(date: LocalDate,selectedDate: LocalDate,isCurrentMonth: Boolean,onSelectDate: (LocalDate) -> Unit
) {val isSelected = date.equals(selectedDate)val isToday = date.equals(LocalDate.now())val isWeekend = date.dayOfWeek == DayOfWeek.SATURDAY || date.dayOfWeek == DayOfWeek.SUNDAYval isPastDate = date.isBefore(LocalDate.now())Box(modifier = Modifier.aspectRatio(1f).padding(4.dp).clip(CircleShape).background(when {isSelected -> Color(0xFF42A5F5)isToday -> Color(0xFFBBDEFB)else -> Color.Transparent}).border(width = if (isToday && !isSelected) 1.dp else 0.dp,color = if (isToday && !isSelected) Color(0xFF42A5F5) else Color.Transparent,shape = CircleShape).clickable { onSelectDate(date) },contentAlignment = Alignment.Center) {Text(text = date.dayOfMonth.toString(),color = when {isSelected -> Color.White!isCurrentMonth -> Color(0xFFBDBDBD) // 非当前月的日期显示为浅灰色isPastDate -> Color(0xFF9E9E9E) // 过去的日期显示为灰色isWeekend -> Color(0xFFE57373) // 周末显示为红色else -> Color(0xFF333333) // 未来的日期显示为黑色},fontSize = 14.sp,fontWeight = if (isToday) FontWeight.Bold else FontWeight.Normal)}
} 
import androidx.lifecycle.LifecycleOwner
import com.fj.test.base.BaseViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.time.LocalDateclass CollapsibleCalendarViewModel : BaseViewModel() {// 当前显示的年月private val _currentMonth = MutableStateFlow(LocalDate.now().withDayOfMonth(1))val currentMonth: StateFlow<LocalDate> = _currentMonth// 是否展开日历private val _isExpanded = MutableStateFlow(false)val isExpanded: StateFlow<Boolean> = _isExpanded// 选中的日期private val _selectedDate = MutableStateFlow(LocalDate.now())val selectedDate: StateFlow<LocalDate> = _selectedDate// 切换上个月或上周(根据展开状态)fun prevMonth() {if (_isExpanded.value) {// 展开状态:切换到上个月_currentMonth.value = _currentMonth.value.minusMonths(1)} else {// 折叠状态:切换到上周_selectedDate.value = _selectedDate.value.minusWeeks(1)}}// 切换下个月或下周(根据展开状态)fun nextMonth() {if (_isExpanded.value) {// 展开状态:切换到下个月_currentMonth.value = _currentMonth.value.plusMonths(1)} else {// 折叠状态:切换到下周_selectedDate.value = _selectedDate.value.plusWeeks(1)}}// 展开/收起日历fun toggleExpand() {_isExpanded.value = !_isExpanded.value// 展开时确保当前月份与选中日期一致if (_isExpanded.value) {_currentMonth.value = LocalDate.of(_selectedDate.value.year,_selectedDate.value.month,1)}}// 选择日期fun selectDate(date: LocalDate) {_selectedDate.value = date}
} 

核心代码我都给出来,这项目采用MVVM的架构,代码这里可以更改状态

 Text(text = date.dayOfMonth.toString(),color = when {isSelected -> Color.White!isCurrentMonth -> Color(0xFFBDBDBD) // 非当前月的日期显示为浅灰色isPastDate -> Color(0xFF9E9E9E) // 过去的日期显示为灰色isWeekend -> Color(0xFFE57373) // 周末显示为红色else -> Color(0xFF333333) // 未来的日期显示为黑色},fontSize = 14.sp,fontWeight = if (isToday) FontWeight.Bold else FontWeight.Normal)

你可以根据数据更改不同颜色,比如,我希望过去是灰色,但是没到的日期都是黑色,你就改这里!isCurrentMonth -> Color(0xFFBDBDBD) // 非当前月的日期显示为浅灰色代码,【这里我改为0xFF333333】变成以下这样的
在这里插入图片描述

那么未到的日期都是是0xFF333333颜色,过去日期都是0xFF9E9E9E颜色,好了,你也可以自己预留接口,改变某天日期的背景色,这个需要你自己动手修改代码。

博主幸劳,转载记得标注原出处链接,支持原创。

http://www.xdnf.cn/news/970831.html

相关文章:

  • IOC和AOP
  • vue实现气泡词云图
  • FastJson的反序列化问题入门
  • Qt使用ODBC连接MySQL数据库
  • R7-1 显示Pascal三角形
  • 【代码模板】从huggingface加载tokenizer和模型,进行推理
  • idea64.exe.vmoptions配置
  • IDEA中配置HTML和Thymeleaf热部署的步骤
  • 蓝桥杯 2024 15届国赛 A组 儿童节快乐
  • 指针与引用参数传递的区别及内存操作流程详解
  • 分散电站,集中掌控,安科瑞光伏云平台助力企业绿色转型
  • 高通录像功能
  • Vim 光标移动命令总览
  • Java中高并发线程池的相关面试题详解
  • 《ZLMediaKit 全流程实战:从部署到 API 调用与前后端集成》
  • 用 LoRA 对 Qwen2.5-VL 模型进行SFT - FORCE_TORCHRUN=1
  • 条件运算符
  • error: src refspec master does not match any - Git
  • coze的基本使用
  • 从零开始搭建现代化 Monorepo 开发模板:TypeScript + Rollup + Jest + 持续集成完整指南
  • Git操作问题及解决方案-记录5
  • (十)学生端搭建
  • 【SQL学习笔记3】深入理解窗口函数的用法
  • 鹰盾加密器系统黑屏问题的深度解析与处理机制
  • RAG系统向量数据库选型与Prompt Engineering鲁棒性测试实践
  • 10:00开始面试,10:06就出来了,问的问题有点变态。。。
  • 第14篇:数据库中间件的分布式配置与动态路由规则热加载机制
  • vxe-table 如何实现直接渲染输入框控件,不需要点击编辑方式,直接就显示文本框
  • DSL查询文档
  • Android OpenSL ES 音频播放完整实现指南