From 6351caf776cb033a11a2b075d8b27c52b8396b8a Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 15 May 2026 17:41:54 +0800 Subject: [PATCH] Add KDoc for public APIs, suppress monthNumber deprecation, and refine comments Co-Authored-By: Claude Opus 4.7 --- .../plus/rua/project/CalendarViewModel.kt | 57 +++++++++++++++++-- .../plus/rua/project/ui/CalendarMonthPage.kt | 10 ++++ .../plus/rua/project/ui/CalendarMonthView.kt | 11 ++-- .../plus/rua/project/ui/CalendarPager.kt | 20 +++++-- .../plus/rua/project/ui/CalendarUtils.kt | 36 ++++++++++++ 5 files changed, 118 insertions(+), 16 deletions(-) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt index 980bc9c..feea61e 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt @@ -19,6 +19,14 @@ import kotlin.time.Clock import plus.rua.project.ui.COLLAPSE_THRESHOLD import plus.rua.project.ui.FLING_VELOCITY_THRESHOLD_DP +/** + * 日历日期数据,用于网格单元格渲染。 + * + * @param date 日期 + * @param isCurrentMonth 是否属于当前显示月份 + * @param isToday 是否为今天 + * @param isSelected 是否为选中日期 + */ data class CalendarDay( val date: LocalDate, val isCurrentMonth: Boolean, @@ -30,6 +38,7 @@ data class CalendarDay( * 日历状态管理,持有选中日期、折叠状态和 ISO 周号计算逻辑。 * * @param coroutineScope 协程作用域,用于驱动折叠动画 + * @param clock 时钟源,默认系统时钟;测试时可注入固定时钟 */ class CalendarViewModel( private val coroutineScope: CoroutineScope, @@ -47,13 +56,25 @@ class CalendarViewModel( private val _collapseAnimatable = Animatable(0f) val collapseProgress: Float get() = _collapseAnimatable.value - val currentYear: Int get() = selectedDate.year + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val currentMonth: Int get() = selectedDate.month.number + val currentYear: Int get() = selectedDate.year + + /** + * 选中指定日期。 + * + * @param date 目标日期 + */ fun selectDate(date: LocalDate) { selectedDate = date } + /** + * 展开状态下拖拽折叠,delta 正值推动 progress 向 1(折叠方向)。 + * + * @param delta 拖拽增量,已归一化到 [0,1] 区间 + */ fun onDrag(delta: Float) { coroutineScope.launch { val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f) @@ -61,8 +82,13 @@ class CalendarViewModel( } } - // 拖拽超过阈值时自动折叠到周视图,否则回弹到月视图 - // velocityDpPerSec: 松手时的 fling 速度 (dp/s),正值=上滑(折叠方向),负值=下滑(展开方向) + /** + * 展开状态拖拽结束,根据进度和速度决定折叠或回弹。 + * + * 拖拽超过阈值时自动折叠到周视图,否则回弹到月视图。 + * + * @param velocityDpPerSec 松手时的 fling 速度 (dp/s),正值=上滑(折叠方向),负值=下滑(展开方向) + */ fun onDragEnd(velocityDpPerSec: Float = 0f) { coroutineScope.launch { val progress = _collapseAnimatable.value @@ -86,7 +112,11 @@ class CalendarViewModel( } } - // 折叠状态下下拉恢复:delta 为负值(向下拖)推动 progress 向 0 + /** + * 折叠状态下下拉恢复,delta 为负值(向下拖)推动 progress 向 0。 + * + * @param delta 拖拽增量,已归一化到 [0,1] 区间 + */ fun onExpandDrag(delta: Float) { coroutineScope.launch { val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f) @@ -94,8 +124,13 @@ class CalendarViewModel( } } - // 下拉超过阈值时自动展开到月视图,否则回弹到周视图 - // velocityDpPerSec: 同上,正值=上滑,负值=下滑 + /** + * 折叠状态拖拽结束,根据进度和速度决定展开或回弹。 + * + * 下拉超过阈值时自动展开到月视图,否则回弹到周视图。 + * + * @param velocityDpPerSec 松手时的 fling 速度 (dp/s),正值=上滑,负值=下滑 + */ fun onExpandDragEnd(velocityDpPerSec: Float = 0f) { coroutineScope.launch { val progress = _collapseAnimatable.value @@ -149,6 +184,16 @@ class CalendarViewModel( return diff / 7 + 1 } + /** + * 计算给定年月的日历网格数据,包含跨月填充至完整行。 + * + * 网格行数按实际需要计算(4/5/6行),每行7格,首行从该月1号所在周的周一开始。 + * + * @param year 年份 + * @param month 月份(1-12) + * @return 日历网格列表,每项包含日期、是否当月、是否今天、是否选中 + */ + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 fun getMonthDays(year: Int, month: Int): List { val firstOfMonth = LocalDate(year, month, 1) val dayOfWeekOffset = firstOfMonth.dayOfWeek.ordinal diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt index 795f8d3..7594a80 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt @@ -26,7 +26,16 @@ import kotlinx.datetime.plus * 折叠时非选中行高度按 (1-p) 缩放,选中行保持原始高度, * 所有行通过手动 y-offset 定位,形成向选中行收缩的视觉效果。 * + * @param year 年份 + * @param month 月份(1-12) + * @param selectedDate 当前选中日期 + * @param today 今天的日期,用于高亮标记 + * @param onDateClick 日期点击回调 + * @param collapseProgress 折叠进度,0f=展开,1f=折叠 * @param rowHeightPx 从外层传入的锁定行高(像素),折叠过程中不变 + * @param effectiveWeeks 当前有效行数(含翻页插值),用于计算总高度 + * @param onRowHeightMeasured 首次行高测量回调,外层据此锁定行高 + * @param modifier 外部布局修饰符 */ @Composable fun CalendarMonthPage( @@ -143,6 +152,7 @@ private data class DayData( val isCurrentMonth: Boolean ) +@Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 private fun generateMonthDays(year: Int, month: Int): List { val firstOfMonth = LocalDate(year, month, 1) val offset = firstOfMonth.dayOfWeek.ordinal diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt index db2288e..ceffd4e 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -47,6 +47,7 @@ fun CalendarMonthView( val viewModel = remember { CalendarViewModel(coroutineScope) } val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) } val currentYear by remember { derivedStateOf { viewModel.selectedDate.year } } + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val currentMonth by remember { derivedStateOf { viewModel.selectedDate.month.number } } val density = LocalDensity.current @@ -64,6 +65,7 @@ fun CalendarMonthView( val cardGapPx = with(density) { lerp(CARD_GAP_EXPANDED_DP.toFloat(), CARD_GAP_COLLAPSED_DP.toFloat(), collapseProgress).dp.toPx() }.toInt() // 翻页时在相邻月份行数之间插值,使 BottomCard 高度平滑过渡 + // abs(fraction) > 阈值时启用插值,避免静止时的浮点抖动 val interpolatedWeeks by remember { derivedStateOf { val fraction = pagerState.currentPageOffsetFraction @@ -80,7 +82,7 @@ fun CalendarMonthView( } // 预估行高:DayCell aspectRatio=1,宽度 = (screenWidth - horizontalPadding) / 7 - // 加上 Row 的 vertical padding (4dp × 2) + // 加上 Row 的 vertical padding (6dp × 2) // 用于 rowHeightPx 尚未测量时的 fallback,避免首次布局高度为 0 val estimatedRowHeightPx = if (screenWidthPx > 0) { val cellWidth = (screenWidthPx - with(density) { (HORIZONTAL_PADDING_DP * 2).dp.toPx() }) / 7 @@ -95,8 +97,8 @@ fun CalendarMonthView( // 折叠时网格高度公式(与 CalendarMonthPage 一致): // collapseProgress=0 展开时 gridH = rowH × weeks;collapseProgress=1 折叠时 gridH = rowH × 1 // 中间态:gridH = rowH × (1 + (weeks-1) × (1-collapseProgress)) - // 必须直接计算而非 derivedStateOf:effectiveRowHeightPx 依赖 rowHeightPx state, - // derivedStateOf 无法追踪非 State 局部变量变化,导致 rowHeightPx 从 0 变为测量值时 gridHeightPx 不更新 + // 直接计算而非 derivedStateOf:effectiveRowHeightPx 依赖 rowHeightPx state, + // derivedStateOf 无法追踪非 State 局部变量,rowHeightPx 从 0 变为测量值时 gridHeightPx 不会更新 val gridHeightPx = if (effectiveRowHeightPx > 0) { val rowH = effectiveRowHeightPx.toFloat() if (collapseProgress > OFFSET_FRACTION_THRESHOLD) { @@ -110,7 +112,7 @@ fun CalendarMonthView( val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx + cardGapPx val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0 - // 行高已知时约束 pager 高度,防止内容溢出;否则让 pager 自由扩展以触发首次行高测量 + // 行高已知时约束 pager 高度防止内容溢出;否则让 pager 自由扩展以触发首次行高测量 val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) { Modifier .height(with(density) { gridHeightPx.toDp() }) @@ -164,6 +166,7 @@ fun CalendarMonthView( onDateClick = { date -> viewModel.selectDate(date) }, onMonthChanged = { year, month -> // 优先选中当月内的今天,否则选中该月1号 + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val date = if (year == today.year && today.month.number == month) today else LocalDate(year, month, 1) viewModel.selectDate(date) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt index f52bf50..d8bc489 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt @@ -17,12 +17,18 @@ import kotlinx.datetime.number /** * 月度日历分页器,HorizontalPager 实现无限左右滑动切换月份。 * + * 使用 Int.MAX_VALUE 页数,中心页为起始月份。点击跨月日期时自动滚动到对应页。 + * 跳过初始 snapshotFlow 发射以保留"今天"选中状态。 + * * @param selectedDate 当前选中日期 * @param today 今天的日期 * @param onDateClick 日期点击回调 - * @param onMonthChanged 月份切换回调,滑动到新月份时触发 + * @param onMonthChanged 月份切换回调,滑动到新月份稳定后触发 * @param collapseProgress 折叠进度,0f=展开,1f=折叠 - * @param rowHeightPx 从外层传入的锁定行高(像素),折叠过程中不变 + * @param rowHeightPx 锁定行高(像素) + * @param effectiveWeeks 当前有效行数(含翻页插值) + * @param onRowHeightMeasured 首次行高测量回调 + * @param pagerState 外层共享的 PagerState,用于保持翻页状态 * @param modifier 外部布局修饰符 */ @Composable @@ -39,10 +45,11 @@ fun CalendarPager( modifier: Modifier = Modifier ) { val initialYear = remember { today.year } + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val initialMonth = remember { today.month.number } val coroutineScope = rememberCoroutineScope() - // Sync settled page to onMonthChanged (skip initial emission to preserve "today" selection) + // 跳过初始发射,保留首次渲染时的"今天"选中状态 LaunchedEffect(pagerState) { snapshotFlow { pagerState.settledPage }.drop(1).collect { page -> val yearMonth = pageToYearMonth(page, initialYear, initialMonth) @@ -63,9 +70,10 @@ fun CalendarPager( selectedDate = selectedDate, today = today, onDateClick = { date -> - onDateClick(date) - // If clicking a date in a different month, scroll to that page - val clickedYear = date.year + onDateClick(date) + // 点击跨月日期时,滚动到该月对应的页 + val clickedYear = date.year + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val clickedMonth = date.month.number if (clickedYear != year || clickedMonth != month) { val targetPage = yearMonthToPage(clickedYear, clickedMonth, initialYear, initialMonth) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt index 380643b..0ab15e4 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarUtils.kt @@ -38,6 +38,10 @@ fun lerp(start: Float, end: Float, fraction: Float): Float = start + (end - star /** * 计算月份在日历网格中需要的行数(4/5/6)。 + * + * @param year 年份 + * @param month 月份(1-12) + * @return 网格行数 */ fun calculateWeeksCount(year: Int, month: Int): Int { val firstOfMonth = LocalDate(year, month, 1) @@ -49,9 +53,14 @@ fun calculateWeeksCount(year: Int, month: Int): Int { /** * 根据 pager 页码计算该页月份的行数。 + * + * @param page 分页器页码 + * @param today 今天的日期,用于确定起始月份 + * @return 网格行数 */ fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int { val initialYear = today.year + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val initialMonth = today.month.number val offset = page - START_PAGE val totalMonths = initialYear * 12 + (initialMonth - 1) + offset @@ -62,6 +71,14 @@ fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int { /** * 页码转年月。 + * + * 中心页 (Int.MAX_VALUE/2) 对应起始月份,向左递减、向右递增, + * 自动处理跨年(12月→1月)。 + * + * @param page 分页器页码 + * @param initialYear 起始年份(中心页对应的年份) + * @param initialMonth 起始月份(中心页对应的月份,1-12) + * @return Pair(year, month) */ fun pageToYearMonth(page: Int, initialYear: Int, initialMonth: Int): Pair { val offset = page - START_PAGE @@ -71,6 +88,14 @@ fun pageToYearMonth(page: Int, initialYear: Int, initialMonth: Int): Pair