修复年视图布局和动画问题

- 年视图每个小月历添加星期行头部
- 日期列用 weight(1f) 对齐,去掉 CircleShape 裁剪
- 取消前一个动画 Job 防止快速点击时动画丢失
This commit is contained in:
meyou 2026-05-16 16:49:10 +08:00
parent 8dad07c0a0
commit 142d0c235a
2 changed files with 38 additions and 15 deletions

View File

@ -8,6 +8,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
@ -58,6 +59,8 @@ class CalendarViewModel(
private val _collapseAnimatable = Animatable(0f)
val collapseProgress: Float get() = _collapseAnimatable.value
private var yearViewJob: Job? = null
@Suppress("DEPRECATION") // monthNumber 无替代 APIkotlinx-datetime 尚未提供新接口
val currentMonth: Int get() = selectedDate.month.number
@ -87,7 +90,8 @@ class CalendarViewModel(
*/
fun toggleYearView() {
if (isCollapsed) return
coroutineScope.launch {
yearViewJob?.cancel()
yearViewJob = coroutineScope.launch {
if (isYearView) {
_yearViewAnimatable.animateTo(
0f, tween(400, easing = FastOutSlowInEasing)
@ -112,7 +116,8 @@ class CalendarViewModel(
val date = if (yearViewYear == today.year && today.month.number == month) today
else LocalDate(yearViewYear, month, 1)
selectedDate = date
coroutineScope.launch {
yearViewJob?.cancel()
yearViewJob = coroutineScope.launch {
_yearViewAnimatable.animateTo(
0f, tween(400, easing = FastOutSlowInEasing)
)

View File

@ -27,16 +27,18 @@ import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus
import kotlinx.datetime.number
import kotlinx.datetime.plus
import kotlinx.datetime.todayIn
private val WEEKDAY_LABELS = listOf("", "", "", "", "", "", "")
/**
* 年度网格视图显示 4×3 精简月历网格支持年份切换
*
* 每格显示一个精简版月历月份标题 + 日期数字网格
* 每格显示一个精简版月历月份标题 + 星期行 + 日期数字网格
* 选中月份高亮点击进入该月
*
* @param year 显示的年份
* @param selectedMonth 当前选中月份1-12
* @param today 今天的日期
* @param onMonthClick 月份点击回调
* @param onYearChange 年份切换回调
* @param modifier 外部布局修饰符
@ -95,7 +97,7 @@ fun YearGridView(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 8.dp),
.padding(horizontal = 4.dp),
verticalArrangement = Arrangement.SpaceEvenly
) {
(0 until 4).forEach { row ->
@ -121,7 +123,7 @@ fun YearGridView(
}
/**
* 精简版月历月份标题 + 日期数字网格
* 精简版月历月份标题 + 星期行 + 日期数字网格
*/
@Composable
private fun MiniMonth(
@ -138,6 +140,7 @@ private fun MiniMonth(
} else {
MaterialTheme.colorScheme.onSurface
}
val weekdayColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f)
val dayColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
val otherMonthColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f)
val todayBgColor = MaterialTheme.colorScheme.primary
@ -145,19 +148,33 @@ private fun MiniMonth(
Column(
modifier = modifier
.padding(2.dp)
.clip(CircleShape)
.clickable(onClick = onClick)
.padding(vertical = 4.dp),
.padding(vertical = 2.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 月份标题
Text(
text = "${month}",
color = titleColor,
fontSize = 10.sp,
fontSize = 9.sp,
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal,
textAlign = TextAlign.Center
)
// 星期行
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
WEEKDAY_LABELS.forEach { label ->
Text(
text = label,
color = weekdayColor,
fontSize = 6.sp,
textAlign = TextAlign.Center,
modifier = Modifier.weight(1f)
)
}
}
// 日期网格
days.chunked(7).forEach { week ->
Row(
@ -165,14 +182,15 @@ private fun MiniMonth(
horizontalArrangement = Arrangement.SpaceEvenly
) {
week.forEach { dayData ->
val isToday = dayData.date == today
val isToday = dayData.date == today && dayData.isCurrentMonth
val color = when {
!dayData.isCurrentMonth -> otherMonthColor
isToday -> MaterialTheme.colorScheme.primary
isToday -> MaterialTheme.colorScheme.onPrimary
else -> dayColor
}
Box(
contentAlignment = Alignment.Center
contentAlignment = Alignment.Center,
modifier = Modifier.weight(1f)
) {
if (isToday) {
Box(
@ -189,10 +207,10 @@ private fun MiniMonth(
}
Text(
text = if (dayData.isCurrentMonth) dayData.date.day.toString() else "",
color = if (isToday) MaterialTheme.colorScheme.onPrimary else color,
fontSize = 7.sp,
color = color,
fontSize = 6.sp,
textAlign = TextAlign.Center,
lineHeight = 10.sp
lineHeight = 9.sp
)
}
}