年视图支持左右滑动切换年份

使用 HorizontalPager 包裹年视图,支持手势滑动切年。
‹ › 按钮改为 animateScrollToPage,与滑动行为一致。
This commit is contained in:
meyou 2026-05-16 16:43:32 +08:00
parent 502f1efc0a
commit 8dad07c0a0
2 changed files with 61 additions and 23 deletions

View File

@ -71,7 +71,7 @@ class CalendarViewModel(
@Suppress("DEPRECATION") // monthNumber 无替代 API @Suppress("DEPRECATION") // monthNumber 无替代 API
var yearViewYear by mutableStateOf(today.year) var yearViewYear by mutableStateOf(today.year)
private set internal set
/** /**
* 选中指定日期 * 选中指定日期

View File

@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerDefaults
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -16,6 +18,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
@ -65,6 +68,32 @@ fun CalendarMonthView(
val pagerState = rememberPagerState(initialPage = START_PAGE, pageCount = { Int.MAX_VALUE }) val pagerState = rememberPagerState(initialPage = START_PAGE, pageCount = { Int.MAX_VALUE })
// 年视图分页器
val yearPagerState = rememberPagerState(
initialPage = START_PAGE,
pageCount = { Int.MAX_VALUE }
)
// 进入年视图时同步 yearPagerState 到当前年
LaunchedEffect(viewModel.isYearView) {
if (viewModel.isYearView) {
if (yearPagerState.currentPage != START_PAGE) {
yearPagerState.scrollToPage(START_PAGE)
}
}
}
// 年视图翻页时同步 yearViewYear
LaunchedEffect(yearPagerState) {
snapshotFlow { yearPagerState.settledPage }.collect { page ->
val offset = page - START_PAGE
val targetYear = viewModel.selectedDate.year + offset
if (targetYear != viewModel.yearViewYear) {
viewModel.yearViewYear = targetYear
}
}
}
// 折叠态 WeekPager 切月时,持续同步 CalendarPager 的 pagerState // 折叠态 WeekPager 切月时,持续同步 CalendarPager 的 pagerState
LaunchedEffect(viewModel.selectedDate) { LaunchedEffect(viewModel.selectedDate) {
@Suppress("DEPRECATION") // monthNumber 无替代 API @Suppress("DEPRECATION") // monthNumber 无替代 API
@ -241,15 +270,29 @@ fun CalendarMonthView(
} }
} }
// 年视图层 // 年视图层HorizontalPager 支持左右滑动切年
if (viewModel.isYearView || yearProgress > 0.01f) { if (viewModel.isYearView || yearProgress > 0.01f) {
HorizontalPager(
state = yearPagerState,
beyondViewportPageCount = 1,
flingBehavior = PagerDefaults.flingBehavior(state = yearPagerState),
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
scaleX = yearScale
scaleY = yearScale
alpha = yearAlpha
transformOrigin = TransformOrigin(anchorPivotX, anchorPivotY)
}
.padding(horizontal = HORIZONTAL_PADDING_DP.dp)
) { page ->
val pageYear = viewModel.selectedDate.year + (page - START_PAGE)
YearGridView( YearGridView(
year = viewModel.yearViewYear, year = pageYear,
selectedMonth = if (viewModel.yearViewYear == currentYear) currentMonth else 0, selectedMonth = if (pageYear == currentYear) currentMonth else 0,
today = today, today = today,
onMonthClick = { month -> onMonthClick = { month ->
viewModel.selectMonthFromYearView(month) viewModel.selectMonthFromYearView(month)
// 同步 CalendarPager 到目标月份
@Suppress("DEPRECATION") // monthNumber 无替代 API @Suppress("DEPRECATION") // monthNumber 无替代 API
val targetPage = yearMonthToPage( val targetPage = yearMonthToPage(
viewModel.yearViewYear, month, viewModel.yearViewYear, month,
@ -260,20 +303,15 @@ fun CalendarMonthView(
} }
}, },
onYearChange = { newYear -> onYearChange = { newYear ->
if (newYear > viewModel.yearViewYear) viewModel.incrementYear() val offset = newYear - pageYear
else viewModel.decrementYear() val targetPage = yearPagerState.currentPage + offset
}, if (targetPage != yearPagerState.currentPage) {
modifier = Modifier coroutineScope.launch { yearPagerState.animateScrollToPage(targetPage) }
.fillMaxSize() }
.graphicsLayer {
scaleX = yearScale
scaleY = yearScale
alpha = yearAlpha
transformOrigin = TransformOrigin(anchorPivotX, anchorPivotY)
} }
.padding(horizontal = HORIZONTAL_PADDING_DP.dp)
) )
} }
}
// BottomCard年视图时隐藏 // BottomCard年视图时隐藏
if (yearProgress < 0.01f) { if (yearProgress < 0.01f) {