From c651a74b9f6ba52fbfa457635fd40a51dd0bae32 Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 19 May 2026 18:24:51 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20=E6=8F=90=E5=8F=96=20CalendarPa?= =?UTF-8?q?gerArea=20=E4=B8=8E=20BottomCardArea=20=E7=8B=AC=E7=AB=8B=20Com?= =?UTF-8?q?posable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 CalendarMonthView 中翻页区域与底部卡片逻辑提取为独立 Composable,减少主函数复杂度,移除不再使用的尺寸测量状态。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../plus/rua/project/ui/CalendarMonthView.kt | 284 +++++++++--------- 1 file changed, 145 insertions(+), 139 deletions(-) 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 a43b1c6..aa2b381 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerDefaults +import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -90,12 +91,8 @@ fun CalendarMonthView( val currentMonth by remember { derivedStateOf { viewModel.selectedDate.month.number } } val density = LocalDensity.current - var monthHeaderHeightPx by remember { mutableIntStateOf(0) } - var weekdayHeaderHeightPx by remember { mutableIntStateOf(0) } var rowHeightPx by remember { mutableIntStateOf(0) } var screenWidthPx by remember { mutableIntStateOf(0) } - var screenHeightPx by remember { mutableIntStateOf(0) } - var calendarContentHeightPx by remember { mutableIntStateOf(0) } var isMenuExpanded by remember { mutableStateOf(false) } // 视图切换时自动关闭菜单 LaunchedEffect(viewModel.isYearView) { @@ -142,64 +139,6 @@ fun CalendarMonthView( } } - val collapseProgress = viewModel.collapseProgress - val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx - - // 预计算固定 dp→px,避免每帧重复 density 转换 - val cardGapExpandedPx = remember { with(density) { CARD_GAP_EXPANDED_DP.dp.toPx() } } - val cardGapCollapsedPx = remember { with(density) { CARD_GAP_COLLAPSED_DP.dp.toPx() } } - val rowPaddingPx = remember { with(density) { ROW_PADDING_DP.dp.toPx() } }.toInt() - val cardGapPx = lerp(cardGapExpandedPx, cardGapCollapsedPx, collapseProgress).toInt() - - val interpolatedWeeks by remember { - derivedStateOf { - val fraction = pagerState.currentPageOffsetFraction - if (abs(fraction) > OFFSET_FRACTION_THRESHOLD) { - val cp = pagerState.currentPage - val baseWeeks = calculateWeeksCountForPage(cp, today) - val targetPage = cp + if (fraction > 0) 1 else -1 - val targetWeeks = calculateWeeksCountForPage(targetPage, today) - lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(fraction)) - } else { - calculateWeeksCountForPage(pagerState.currentPage, today).toFloat() - } - } - } - - // 预计算固定 dp→px,避免每帧重复 density 转换 - val horizontalPaddingPx = remember { with(density) { (HORIZONTAL_PADDING_DP * 2).dp.toPx() } } - val rowPadding2Px = remember { with(density) { (ROW_PADDING_DP * 2).dp.toPx() } } - - val estimatedRowHeightPx = if (screenWidthPx > 0) { - val cellWidth = (screenWidthPx - horizontalPaddingPx) / 7 - (cellWidth + rowPadding2Px).toInt() - } else 0 - - val effectiveRowHeightPx = if (rowHeightPx > 0) rowHeightPx else estimatedRowHeightPx - val effectiveWeeks = interpolatedWeeks - - val gridHeightPx = if (effectiveRowHeightPx > 0) { - val rowH = effectiveRowHeightPx.toFloat() - if (collapseProgress > OFFSET_FRACTION_THRESHOLD) { - (rowH * (1 + (effectiveWeeks - 1) * (1f - collapseProgress))).toInt() - } else { - (rowH * effectiveWeeks).toInt() - } - } else 0 - - val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx + cardGapPx - - val cardHeightPx = - if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0 - - val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) { - Modifier - .height(with(density) { gridHeightPx.toDp() }) - .clipToBounds() - } else { - Modifier - } - // 年视图锚点缩放:当前月在 4×3 网格中的归一化位置 val anchorPivotX = ((currentMonth - 1) % 3 + 0.5f) / 3f val anchorPivotY = ((currentMonth - 1) / 3 + 0.5f) / 4f @@ -211,27 +150,17 @@ fun CalendarMonthView( .statusBarsPadding() .onSizeChanged { size -> screenWidthPx = size.width - screenHeightPx = size.height } ) { // 月视图层:仅在非年视图时渲染,年视图激活时立即移除。 if (!viewModel.isYearView) { composeTraceBeginSection("MonthView:Compose") - val dragRangeMinPx = with(density) { DRAG_RANGE_MIN_DP.dp.toPx() } - val dragRangePx = if (effectiveRowHeightPx > 0) { - maxOf((effectiveWeeks - 1) * effectiveRowHeightPx.toFloat(), dragRangeMinPx) - } else { - dragRangeMinPx - } - - val monthProgress = 1f - viewModel.yearViewProgress - // 组合阶段计算:lambda 捕获快照值,避免 draw 阶段读到已更新的 rowHeightPx - // 但 layout 仍用旧值导致行堆叠 val layoutReady = rowHeightPx > 0 Box( modifier = Modifier .fillMaxSize() .graphicsLayer { + val monthProgress = 1f - viewModel.yearViewProgress val scale = lerp(0.3f, 1f, monthProgress) scaleX = scale scaleY = scale @@ -251,79 +180,27 @@ fun CalendarMonthView( showToday = viewModel.selectedDate != today, onToday = { viewModel.selectDate(today) - }, - modifier = Modifier.onSizeChanged { size -> - monthHeaderHeightPx = size.height } ) WeekdayHeader( modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp) - .onSizeChanged { size -> - weekdayHeaderHeightPx = size.height - } ) - if (viewModel.isCollapsed && viewModel.collapseProgress >= 1f) { - WeekPager( - selectedDate = viewModel.selectedDate, - today = today, - onDateClick = { date -> viewModel.selectDate(date) }, - onWeekChanged = { weekMonday -> - val weekSunday = weekMonday.plus(DatePeriod(days = 6)) - val date = when { - today in weekMonday..weekSunday -> today - weekMonday.month != weekSunday.month -> { - if (weekMonday < viewModel.selectedDate) { - @Suppress("DEPRECATION") // monthNumber 无替代 API - LocalDate(weekSunday.year, weekSunday.month.number, 1) - } else { - weekMonday - } - } - - else -> weekMonday - } - viewModel.selectDate(date) - }, - shiftKindAt = { date -> viewModel.shiftKindAt(date) }, - showLegalHoliday = viewModel.showLegalHoliday, - modifier = pagerModifier - ) - } else { - CalendarPager( - selectedDate = viewModel.selectedDate, - today = today, - onDateClick = { date -> viewModel.selectDate(date) }, - onMonthChanged = { year, month -> - @Suppress("DEPRECATION") // monthNumber 无替代 API - val date = - if (year == today.year && today.month.number == month) today - else LocalDate(year, month, 1) - viewModel.selectDate(date) - }, - collapseProgress = viewModel.collapseProgress, - rowHeightPx = rowHeightPx, - effectiveWeeks = effectiveWeeks, - shiftKindAt = { date -> viewModel.shiftKindAt(date) }, - showLegalHoliday = viewModel.showLegalHoliday, - onRowHeightMeasured = { h -> - if (h > 0) rowHeightPx = h - }, - pagerState = pagerState, - modifier = pagerModifier - ) - } - } - - if (cardHeightPx > 0) { - BottomCard( + CalendarPagerArea( viewModel = viewModel, - selectedDate = viewModel.selectedDate, today = today, - dragRangePx = dragRangePx, - modifier = Modifier - .fillMaxWidth() - .height(with(density) { cardHeightPx.toDp() }) - .align(Alignment.BottomCenter) + rowHeightPx = rowHeightPx, + screenWidthPx = screenWidthPx, + onRowHeightMeasured = { h -> + if (h > 0) rowHeightPx = h + }, + pagerState = pagerState, + modifier = Modifier.clipToBounds() + ) + BottomCardArea( + viewModel = viewModel, + today = today, + rowHeightPx = rowHeightPx, + modifier = Modifier.fillMaxWidth() ) } } @@ -498,6 +375,135 @@ private fun MenuIcon(modifier: Modifier = Modifier) { } } +@Composable +private fun CalendarPagerArea( + viewModel: CalendarViewModel, + today: LocalDate, + rowHeightPx: Int, + screenWidthPx: Int, + onRowHeightMeasured: ((Int) -> Unit)?, + pagerState: PagerState, + modifier: Modifier = Modifier +) { + val density = LocalDensity.current + val collapseProgress = viewModel.collapseProgress + + val interpolatedWeeks by remember { + derivedStateOf { + val fraction = pagerState.currentPageOffsetFraction + if (abs(fraction) > OFFSET_FRACTION_THRESHOLD) { + val cp = pagerState.currentPage + val baseWeeks = calculateWeeksCountForPage(cp, today) + val targetPage = cp + if (fraction > 0) 1 else -1 + val targetWeeks = calculateWeeksCountForPage(targetPage, today) + lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(fraction)) + } else { + calculateWeeksCountForPage(pagerState.currentPage, today).toFloat() + } + } + } + + val horizontalPaddingPx = remember { with(density) { (HORIZONTAL_PADDING_DP * 2).dp.toPx() } } + val rowPadding2Px = remember { with(density) { (ROW_PADDING_DP * 2).dp.toPx() } } + + val estimatedRowHeightPx = if (screenWidthPx > 0) { + val cellWidth = (screenWidthPx - horizontalPaddingPx) / 7 + (cellWidth + rowPadding2Px).toInt() + } else 0 + + val effectiveRowHeightPx = if (rowHeightPx > 0) rowHeightPx else estimatedRowHeightPx + val effectiveWeeks = interpolatedWeeks + + val gridHeightPx = if (effectiveRowHeightPx > 0) { + val rowH = effectiveRowHeightPx.toFloat() + if (collapseProgress > OFFSET_FRACTION_THRESHOLD) { + (rowH * (1 + (effectiveWeeks - 1) * (1f - collapseProgress))).toInt() + } else { + (rowH * effectiveWeeks).toInt() + } + } else 0 + + val pagerModifier = if (rowHeightPx > 0 && gridHeightPx > 0) { + Modifier + .height(with(density) { gridHeightPx.toDp() }) + .then(modifier) + } else { + modifier + } + + if (viewModel.isCollapsed && collapseProgress >= 1f) { + WeekPager( + selectedDate = viewModel.selectedDate, + today = today, + onDateClick = { date -> viewModel.selectDate(date) }, + onWeekChanged = { weekMonday -> + val weekSunday = weekMonday.plus(DatePeriod(days = 6)) + val date = when { + today in weekMonday..weekSunday -> today + weekMonday.month != weekSunday.month -> { + if (weekMonday < viewModel.selectedDate) { + @Suppress("DEPRECATION") // monthNumber 无替代 API + LocalDate(weekSunday.year, weekSunday.month.number, 1) + } else { + weekMonday + } + } + else -> weekMonday + } + viewModel.selectDate(date) + }, + shiftKindAt = { date -> viewModel.shiftKindAt(date) }, + showLegalHoliday = viewModel.showLegalHoliday, + modifier = pagerModifier + ) + } else { + CalendarPager( + selectedDate = viewModel.selectedDate, + today = today, + onDateClick = { date -> viewModel.selectDate(date) }, + onMonthChanged = { year, month -> + @Suppress("DEPRECATION") // monthNumber 无替代 API + val date = + if (year == today.year && today.month.number == month) today + else LocalDate(year, month, 1) + viewModel.selectDate(date) + }, + collapseProgress = collapseProgress, + rowHeightPx = rowHeightPx, + effectiveWeeks = effectiveWeeks, + shiftKindAt = { date -> viewModel.shiftKindAt(date) }, + showLegalHoliday = viewModel.showLegalHoliday, + onRowHeightMeasured = onRowHeightMeasured, + pagerState = pagerState, + modifier = pagerModifier + ) + } +} + +@Composable +private fun BottomCardArea( + viewModel: CalendarViewModel, + today: LocalDate, + rowHeightPx: Int, + modifier: Modifier = Modifier +) { + val density = LocalDensity.current + val dragRangeMinPx = remember { with(density) { DRAG_RANGE_MIN_DP.dp.toPx() } } + val dragRangePx = if (rowHeightPx > 0) { + maxOf(4f * rowHeightPx, dragRangeMinPx) + } else { + dragRangeMinPx + } + + BottomCard( + viewModel = viewModel, + selectedDate = viewModel.selectedDate, + today = today, + dragRangePx = dragRangePx, + modifier = modifier + ) +} + @Composable private fun MenuItem( text: String, From e5ce11128ad42cc0e4c7b462f8668678d8d30dd3 Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 19 May 2026 18:29:41 +0800 Subject: [PATCH 2/2] =?UTF-8?q?style:=20=E4=BB=A3=E7=A0=81=E6=B8=85?= =?UTF-8?q?=E7=90=86=20=E2=80=94=20=E7=A7=BB=E9=99=A4=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=20import=20=E5=92=8C=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Platform.android.kt: 移除未使用的 kotlinx.coroutines.launch import 和未使用的 scope 变量 - CalendarMonthView.kt: 移除未使用的 density 变量 - App.kt: 格式化缩进 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../kotlin/plus/rua/project/Platform.android.kt | 3 +-- .../src/commonMain/kotlin/plus/rua/project/App.kt | 14 +++++++++++--- .../plus/rua/project/ui/CalendarMonthView.kt | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt index f6320fd..6b4d872 100644 --- a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt +++ b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt @@ -4,7 +4,6 @@ import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.launch class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" @@ -24,7 +23,7 @@ actual fun PredictiveBackHandler( onCancel: () -> Unit ) { if (Build.VERSION.SDK_INT >= 34) { - val scope = rememberCoroutineScope() + rememberCoroutineScope() androidx.activity.compose.PredictiveBackHandler(enabled) { progress -> try { progress.collect { backEvent -> diff --git a/shared/src/commonMain/kotlin/plus/rua/project/App.kt b/shared/src/commonMain/kotlin/plus/rua/project/App.kt index 68a8521..317e723 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/App.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/App.kt @@ -57,11 +57,17 @@ fun App() { if (targetState.ordinal > initialState.ordinal) { // 正向导航:新页面从右侧滑入覆盖,旧页面略微左移+淡出 (slideInHorizontally { it } + fadeIn()) togetherWith - (slideOutHorizontally { -it / 4 } + fadeOut()) + (slideOutHorizontally { -it / 4 } + fadeOut()) } else { // 返回导航:新页面从左侧滑入,旧页面向右侧滑出 - (slideInHorizontally(animationSpec = tween(250)) { -it } + fadeIn(animationSpec = tween(250))) togetherWith - (slideOutHorizontally(animationSpec = tween(250)) { it } + fadeOut(animationSpec = tween(250))) + (slideInHorizontally(animationSpec = tween(250)) { -it } + fadeIn( + animationSpec = tween( + 250 + ) + )) togetherWith + (slideOutHorizontally(animationSpec = tween(250)) { it } + fadeOut( + animationSpec = tween(250) + )) } }, modifier = Modifier.fillMaxSize() @@ -71,6 +77,7 @@ fun App() { modifier = Modifier, onNavigateToAbout = { currentScreen = Screen.About } ) + Screen.About -> { PredictiveBackHandler( enabled = backProgress == 0f, @@ -88,6 +95,7 @@ fun App() { } ) } + Screen.Licenses -> { PredictiveBackHandler( enabled = backProgress == 0f, 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 aa2b381..b2395ca 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -89,7 +89,7 @@ fun CalendarMonthView( @Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口 val currentMonth by remember { derivedStateOf { viewModel.selectedDate.month.number } } - val density = LocalDensity.current + LocalDensity.current var rowHeightPx by remember { mutableIntStateOf(0) } var screenWidthPx by remember { mutableIntStateOf(0) } @@ -448,6 +448,7 @@ private fun CalendarPagerArea( weekMonday } } + else -> weekMonday } viewModel.selectDate(date)