Add debug logging and fix gridHeightPx derivedStateOf state tracking

gridHeightPx changed from derivedStateOf to direct computation because
derivedStateOf cannot track non-State local variable changes, causing
gridHeightPx to not update when rowHeightPx transitions from 0 to measured value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-15 11:21:29 +08:00
parent ddc852a667
commit c74de5f151
4 changed files with 33 additions and 12 deletions

View File

@ -47,12 +47,14 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
val currentMonth: Int get() = selectedDate.month.number val currentMonth: Int get() = selectedDate.month.number
fun selectDate(date: LocalDate) { fun selectDate(date: LocalDate) {
println("CalendarViewModel: selectDate $date (was $selectedDate)")
selectedDate = date selectedDate = date
} }
fun onDrag(delta: Float) { fun onDrag(delta: Float) {
coroutineScope.launch { coroutineScope.launch {
val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f) val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f)
println("CalendarViewModel: onDrag delta=$delta, progress=${_collapseAnimatable.value} -> $new")
_collapseAnimatable.snapTo(new) _collapseAnimatable.snapTo(new)
} }
} }
@ -61,17 +63,20 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
fun onDragEnd() { fun onDragEnd() {
coroutineScope.launch { coroutineScope.launch {
val current = _collapseAnimatable.value val current = _collapseAnimatable.value
println("CalendarViewModel: onDragEnd current=$current, threshold=$COLLAPSE_THRESHOLD")
if (current > COLLAPSE_THRESHOLD) { if (current > COLLAPSE_THRESHOLD) {
_collapseAnimatable.animateTo( _collapseAnimatable.animateTo(
targetValue = 1f, targetValue = 1f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f) animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
) )
isCollapsed = true isCollapsed = true
println("CalendarViewModel: collapsed=true")
} else { } else {
_collapseAnimatable.animateTo( _collapseAnimatable.animateTo(
targetValue = 0f, targetValue = 0f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f) animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
) )
println("CalendarViewModel: snapped back to 0f")
} }
} }
} }
@ -80,6 +85,7 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
fun onExpandDrag(delta: Float) { fun onExpandDrag(delta: Float) {
coroutineScope.launch { coroutineScope.launch {
val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f) val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f)
println("CalendarViewModel: onExpandDrag delta=$delta, progress=${_collapseAnimatable.value} -> $new")
_collapseAnimatable.snapTo(new) _collapseAnimatable.snapTo(new)
} }
} }
@ -88,17 +94,20 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
fun onExpandDragEnd() { fun onExpandDragEnd() {
coroutineScope.launch { coroutineScope.launch {
val current = _collapseAnimatable.value val current = _collapseAnimatable.value
println("CalendarViewModel: onExpandDragEnd current=$current, threshold=$COLLAPSE_THRESHOLD")
if (current < COLLAPSE_THRESHOLD) { if (current < COLLAPSE_THRESHOLD) {
_collapseAnimatable.animateTo( _collapseAnimatable.animateTo(
targetValue = 0f, targetValue = 0f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f) animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
) )
isCollapsed = false isCollapsed = false
println("CalendarViewModel: expanded=false")
} else { } else {
_collapseAnimatable.animateTo( _collapseAnimatable.animateTo(
targetValue = 1f, targetValue = 1f,
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f) animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
) )
println("CalendarViewModel: snapped back to 1f")
} }
} }
} }

View File

@ -33,6 +33,7 @@ fun BottomCard(
) { ) {
val density = LocalDensity.current val density = LocalDensity.current
val dragRange = with(density) { DRAG_RANGE_DP.dp.toPx() } val dragRange = with(density) { DRAG_RANGE_DP.dp.toPx() }
println("BottomCard: isCollapsed=${viewModel.isCollapsed}, dragRange=$dragRange, progress=${viewModel.collapseProgress}")
Surface( Surface(
modifier = modifier modifier = modifier

View File

@ -115,7 +115,10 @@ fun CalendarMonthPage(
.then( .then(
if (weekIndex == 0 && rowHeightPx == 0) { if (weekIndex == 0 && rowHeightPx == 0) {
Modifier.onSizeChanged { size -> Modifier.onSizeChanged { size ->
if (size.height > 0) onRowHeightMeasured?.invoke(size.height) if (size.height > 0) {
println("CalendarMonthPage: measured rowHeight=${size.height}px, reporting to parent")
onRowHeightMeasured?.invoke(size.height)
}
} }
} else Modifier } else Modifier
) )

View File

@ -31,6 +31,8 @@ import kotlin.math.abs
import kotlin.time.Clock import kotlin.time.Clock
import plus.rua.project.CalendarViewModel import plus.rua.project.CalendarViewModel
private const val TAG = "CalendarMonthView"
/** /**
* 日历主界面包含月/周视图切换和折叠动画 * 日历主界面包含月/周视图切换和折叠动画
* *
@ -59,8 +61,10 @@ fun CalendarMonthView(
val pagerState = rememberPagerState(initialPage = START_PAGE, pageCount = { Int.MAX_VALUE }) val pagerState = rememberPagerState(initialPage = START_PAGE, pageCount = { Int.MAX_VALUE })
val p = viewModel.collapseProgress val p = viewModel.collapseProgress
println("$TAG: collapseProgress=$p, isCollapsed=${viewModel.isCollapsed}")
val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx
val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt() val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt()
println("$TAG: headerHeightPx=$headerHeightPx (month=$monthHeaderHeightPx, weekday=$weekdayHeaderHeightPx), rowPaddingPx=$rowPaddingPx")
val interpolatedWeeks by remember { val interpolatedWeeks by remember {
derivedStateOf { derivedStateOf {
@ -85,27 +89,28 @@ fun CalendarMonthView(
(cellWidth + rowPadding).toInt() (cellWidth + rowPadding).toInt()
} else 0 } else 0
println("$TAG: screenWidthPx=$screenWidthPx, screenHeightPx=$screenHeightPx, estimatedRowHeightPx=$estimatedRowHeightPx, measuredRowHeightPx=$rowHeightPx")
val effectiveRowHeightPx = if (rowHeightPx > 0) rowHeightPx else estimatedRowHeightPx val effectiveRowHeightPx = if (rowHeightPx > 0) rowHeightPx else estimatedRowHeightPx
// 折叠时网格高度公式(与 CalendarMonthPage 一致): // 折叠时网格高度公式(与 CalendarMonthPage 一致):
// gridH = rowH × (1 + (weeks-1) × (1-p)) // gridH = rowH × (1 + (weeks-1) × (1-p))
val effectiveWeeks = interpolatedWeeks val effectiveWeeks = interpolatedWeeks
val gridHeightPx by remember { // gridHeightPx 必须直接计算而非 derivedStateOf因为 effectiveRowHeightPx 依赖 rowHeightPx state
derivedStateOf { // derivedStateOf 无法追踪非 State 的局部变量变化,导致 rowHeightPx 从 0 变为测量值时 gridHeightPx 不更新
if (effectiveRowHeightPx > 0) { val gridHeightPx = if (effectiveRowHeightPx > 0) {
val rowH = effectiveRowHeightPx.toFloat() val rowH = effectiveRowHeightPx.toFloat()
if (p > OFFSET_FRACTION_THRESHOLD) { if (p > OFFSET_FRACTION_THRESHOLD) {
(rowH * (1 + (effectiveWeeks - 1) * (1f - p))).toInt() (rowH * (1 + (effectiveWeeks - 1) * (1f - p))).toInt()
} else { } else {
(rowH * effectiveWeeks).toInt() (rowH * effectiveWeeks).toInt()
}
} else 0
} }
} } else 0
val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx
val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0 val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0
println("$TAG: effectiveRowHeightPx=$effectiveRowHeightPx, effectiveWeeks=$effectiveWeeks, gridHeightPx=$gridHeightPx, calendarAreaHeightPx=$calendarAreaHeightPx, cardHeightPx=$cardHeightPx")
// 当 rowHeightPx 已知时,用计算的高度约束 pager否则让 pager 自由扩展以测量行高 // 当 rowHeightPx 已知时,用计算的高度约束 pager否则让 pager 自由扩展以测量行高
val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) { val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) {
@ -141,6 +146,7 @@ fun CalendarMonthView(
) )
// 完全折叠且无动画时显示 WeekPager否则显示 CalendarPager含下拉恢复过程 // 完全折叠且无动画时显示 WeekPager否则显示 CalendarPager含下拉恢复过程
if (viewModel.isCollapsed && viewModel.collapseProgress >= 1f) { if (viewModel.isCollapsed && viewModel.collapseProgress >= 1f) {
println("$TAG: Showing WeekPager (isCollapsed=true, progress=${viewModel.collapseProgress})")
WeekPager( WeekPager(
selectedDate = viewModel.selectedDate, selectedDate = viewModel.selectedDate,
today = today, today = today,
@ -152,6 +158,7 @@ fun CalendarMonthView(
} }
) )
} else { } else {
println("$TAG: Showing CalendarPager (isCollapsed=${viewModel.isCollapsed}, progress=${viewModel.collapseProgress})")
CalendarPager( CalendarPager(
selectedDate = viewModel.selectedDate, selectedDate = viewModel.selectedDate,
today = today, today = today,
@ -174,6 +181,7 @@ fun CalendarMonthView(
} }
if (cardHeightPx > 0) { if (cardHeightPx > 0) {
println("$TAG: BottomCard height=${with(density) { cardHeightPx.toDp() }}")
BottomCard( BottomCard(
viewModel = viewModel, viewModel = viewModel,
modifier = Modifier modifier = Modifier