From b95f748839d65e0300746b90df98addcdc666938 Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 15 May 2026 00:57:03 +0800 Subject: [PATCH] Measure row height from DayCell instead of reverse-calculating from Column Replace the fragile approach of computing rowHeightPx by dividing Column height by weeks count with direct measurement via onSizeChanged on the first DayCell row. Add estimated row height fallback based on screen width and cell aspect ratio so the pager can be constrained before the first measurement completes. Remove lockedRowHeightPx, expandedWeeksCount, and calendarHeightPx state variables that are no longer needed. Co-Authored-By: Claude Opus 4.7 --- .../plus/rua/project/ui/CalendarMonthPage.kt | 9 +++ .../plus/rua/project/ui/CalendarMonthView.kt | 61 ++++++++++--------- .../plus/rua/project/ui/CalendarPager.kt | 4 +- 3 files changed, 43 insertions(+), 31 deletions(-) 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 989e4d4..28b664c 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex @@ -35,6 +36,7 @@ fun CalendarMonthPage( onDateClick: (LocalDate) -> Unit, collapseProgress: Float, rowHeightPx: Int, + onRowHeightMeasured: ((Int) -> Unit)? = null, modifier: Modifier = Modifier ) { val days = remember(year, month) { @@ -113,6 +115,13 @@ fun CalendarMonthPage( ) .offset(y = yOffsetDp) .padding(vertical = 4.dp) + .then( + if (weekIndex == 0 && rowHeightPx == 0) { + Modifier.onSizeChanged { size -> + if (size.height > 0) onRowHeightMeasured?.invoke(size.height) + } + } else Modifier + ) ) { week.forEach { dayData -> DayCell( 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 1f4eee5..293a740 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -31,6 +31,7 @@ import kotlin.time.Clock import plus.rua.project.CalendarViewModel private const val START_PAGE = Int.MAX_VALUE / 2 +private const val ROW_PADDING_DP = 4 /** * 日历主界面,包含月/周视图切换和折叠动画。 @@ -51,25 +52,19 @@ fun CalendarMonthView( var currentMonth by remember { mutableIntStateOf(viewModel.currentMonth) } val density = LocalDensity.current - var calendarHeightPx by remember { mutableIntStateOf(0) } var monthHeaderHeightPx by remember { mutableIntStateOf(0) } var weekdayHeaderHeightPx by remember { mutableIntStateOf(0) } + var rowHeightPx by remember { mutableIntStateOf(0) } + @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 + var currentWeeksCount by remember { mutableIntStateOf(calculateWeeksCount(today.year, today.monthNumber)) } + var screenWidthPx by remember { mutableIntStateOf(0) } var screenHeightPx by remember { mutableIntStateOf(0) } - var currentWeeksCount by remember { mutableIntStateOf(6) } - var expandedWeeksCount by remember { mutableIntStateOf(6) } - var lockedRowHeightPx by remember { mutableIntStateOf(0) } val pagerState = rememberPagerState(initialPage = START_PAGE, pageCount = { Int.MAX_VALUE }) val p = viewModel.collapseProgress val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx - - // 行高:优先使用锁定值(折叠过程中不变),否则用实时计算初始化 - val rowHeightPx = if (lockedRowHeightPx > 0) { - lockedRowHeightPx - } else if (calendarHeightPx > 0 && expandedWeeksCount > 0) { - (calendarHeightPx - headerHeightPx) / expandedWeeksCount - } else 0 + val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt() // 滑动偏移插值行数 val offsetFraction by remember { derivedStateOf { pagerState.currentPageOffsetFraction } } @@ -81,24 +76,33 @@ fun CalendarMonthView( currentWeeksCount.toFloat() } + // 预估行高:DayCell aspectRatio=1,宽度 = (screenWidth - horizontalPadding) / 7 + // 加上 Row 的 vertical padding (4dp × 2) + val estimatedRowHeightPx = if (screenWidthPx > 0) { + val cellWidth = (screenWidthPx - with(density) { 32.dp.toPx() }) / 7 + val rowPadding = with(density) { 8.dp.toPx() } + (cellWidth + rowPadding).toInt() + } else 0 + + val effectiveRowHeightPx = if (rowHeightPx > 0) rowHeightPx else estimatedRowHeightPx + // 折叠时网格高度公式(与 CalendarMonthPage 一致): // gridH = rowH × (1 + (weeks-1) × (1-p)) - val gridHeightPx = if (rowHeightPx > 0) { - val rowH = rowHeightPx.toFloat() + val gridHeightPx = if (effectiveRowHeightPx > 0) { + val rowH = effectiveRowHeightPx.toFloat() val weeks = interpolatedWeeks - if (p > 0f) { + if (p > 0.01f) { (rowH * (1 + (weeks - 1) * (1f - p))).toInt() } else { (rowH * weeks).toInt() } } else 0 - val rowPaddingPx = with(density) { 4.dp.toPx() }.toInt() + val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx + val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0 - val cardTopPx = headerHeightPx + gridHeightPx + rowPaddingPx - val cardHeightPx = screenHeightPx - cardTopPx - - val pagerModifier = if (p > 0.01f && rowHeightPx > 0) { + // 当 rowHeightPx 已知时,用计算的高度约束 pager;否则让 pager 自由扩展以测量行高 + val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) { Modifier .height(with(density) { gridHeightPx.toDp() }) .clipToBounds() @@ -111,17 +115,11 @@ fun CalendarMonthView( .fillMaxSize() .statusBarsPadding() .onSizeChanged { size -> + screenWidthPx = size.width screenHeightPx = size.height } ) { - Column(modifier = Modifier.padding(horizontal = 16.dp).onSizeChanged { size -> - calendarHeightPx = size.height - if (p < 0.01f) { - expandedWeeksCount = currentWeeksCount - val calculated = (size.height - headerHeightPx) / currentWeeksCount - if (calculated > 0) lockedRowHeightPx = calculated - } - }) { + Column(modifier = Modifier.padding(horizontal = 16.dp)) { MonthHeader( year = currentYear, month = currentMonth, @@ -133,7 +131,7 @@ fun CalendarMonthView( WeekdayHeader( modifier = Modifier.fillMaxWidth().onSizeChanged { size -> weekdayHeaderHeightPx = size.height - }.padding(bottom = 4.dp) + }.padding(bottom = ROW_PADDING_DP.dp) ) // 完全折叠且无动画时显示 WeekPager,否则显示 CalendarPager(含下拉恢复过程) if (viewModel.isCollapsed && viewModel.collapseProgress >= 1f) { @@ -166,13 +164,16 @@ fun CalendarMonthView( rowHeightPx = rowHeightPx, onWeeksChanged = { weeks -> currentWeeksCount = weeks - if (p < 0.01f) expandedWeeksCount = weeks + }, + onRowHeightMeasured = { h -> + if (h > 0 && rowHeightPx == 0) rowHeightPx = h }, pagerState = pagerState, modifier = pagerModifier ) } } + if (cardHeightPx > 0) { BottomCard( viewModel = viewModel, @@ -196,4 +197,4 @@ private fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int { val year = totalMonths / 12 val month = totalMonths % 12 + 1 return calculateWeeksCount(year, month) -} \ No newline at end of file +} 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 28031a4..a14619f 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt @@ -39,6 +39,7 @@ fun CalendarPager( collapseProgress: Float, rowHeightPx: Int, onWeeksChanged: ((Int) -> Unit)? = null, + onRowHeightMeasured: ((Int) -> Unit)? = null, pagerState: PagerState, modifier: Modifier = Modifier ) { @@ -80,7 +81,8 @@ fun CalendarPager( } }, collapseProgress = collapseProgress, - rowHeightPx = rowHeightPx + rowHeightPx = rowHeightPx, + onRowHeightMeasured = onRowHeightMeasured ) } }