perf: DayCell produceState 上移到页面级,减少协程数量

CalendarMonthPage 统一预计算整月 42 天的 lunarData(1 个协程),
通过参数传入 DayCell,避免每个单元格独立启动 produceState(42 协程)。

- holidayBadges produceState 扩展为 lunarDataMap,存储完整 DayCellInfo
- DayCell 添加可选 lunarData 参数,条件分支调用 DayCellImpl
- WeekPager 保持向后兼容(不传 lunarData 时内部获取)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-25 16:44:06 +08:00
parent e175bb07da
commit da46204de4
2 changed files with 66 additions and 17 deletions

View File

@ -25,6 +25,7 @@ import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus
import kotlinx.datetime.number
import kotlinx.datetime.plus
import plus.rua.project.DayCellInfo
import plus.rua.project.LunarCache
import plus.rua.project.ShiftKind
@ -69,27 +70,26 @@ fun CalendarMonthPage(
val density = LocalDensity.current
val interactionSource = remember { MutableInteractionSource() }
val holidayBadges by produceState(
initialValue = emptyMap<LocalDate, String?>(),
val lunarDataMap by produceState(
initialValue = emptyMap<LocalDate, DayCellInfo>(),
key1 = year,
key2 = month
) {
val map = mutableMapOf<LocalDate, String?>()
val map = mutableMapOf<LocalDate, DayCellInfo>()
for (dayData in days) {
val info = LunarCache.default.getOrCompute(dayData.date)
map[dayData.date] = info.holidayBadge
map[dayData.date] = LunarCache.default.getOrCompute(dayData.date)
}
value = map
}
val holidayEdges = remember(holidayBadges, year, month) {
val holidayEdges = remember(lunarDataMap, year, month) {
val map = mutableMapOf<LocalDate, HolidayEdgeInfo>()
for (dayData in days) {
val date = dayData.date
val badge = holidayBadges[date]
val badge = lunarDataMap[date]?.holidayBadge
if (badge == null) continue
val prevBadge = holidayBadges[date.minus(DatePeriod(days = 1))]
val nextBadge = holidayBadges[date.plus(DatePeriod(days = 1))]
val prevBadge = lunarDataMap[date.minus(DatePeriod(days = 1))]?.holidayBadge
val nextBadge = lunarDataMap[date.plus(DatePeriod(days = 1))]?.holidayBadge
map[date] = HolidayEdgeInfo(
isStart = prevBadge != badge,
isEnd = nextBadge != badge
@ -128,6 +128,7 @@ fun CalendarMonthPage(
shiftKindAt = shiftKindAt,
showLegalHoliday = showLegalHoliday,
holidayEdges = holidayEdges,
lunarDataMap = lunarDataMap,
onDateClick = onDateClick,
onRowHeightMeasured = onRowHeightMeasured,
interactionSource = interactionSource
@ -150,6 +151,7 @@ private fun WeekRow(
shiftKindAt: (LocalDate) -> ShiftKind?,
showLegalHoliday: Boolean,
holidayEdges: Map<LocalDate, HolidayEdgeInfo>,
lunarDataMap: Map<LocalDate, DayCellInfo>,
onDateClick: (LocalDate) -> Unit,
onRowHeightMeasured: ((Int) -> Unit)?,
interactionSource: MutableInteractionSource,
@ -235,7 +237,8 @@ private fun WeekRow(
holidayEdgeInfo = holidayEdges[dayData.date],
onClick = { onDateClick(dayData.date) },
modifier = Modifier.weight(1f),
interactionSource = interactionSource
interactionSource = interactionSource,
lunarData = lunarDataMap[dayData.date]
)
}
}

View File

@ -72,15 +72,61 @@ fun DayCell(
onClick: () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
lunarCache: LunarCache = LunarCache.default
lunarCache: LunarCache = LunarCache.default,
lunarData: DayCellInfo? = null,
) {
val lunarData by produceState(
initialValue = DayCellInfo("", false, null),
key1 = date,
key2 = lunarCache
) {
value = lunarCache.getOrCompute(date)
if (lunarData != null) {
DayCellImpl(
date = date,
isCurrentMonth = isCurrentMonth,
isSelected = isSelected,
isToday = isToday,
shiftKind = shiftKind,
showLegalHoliday = showLegalHoliday,
holidayEdgeInfo = holidayEdgeInfo,
onClick = onClick,
modifier = modifier,
interactionSource = interactionSource,
lunarData = lunarData,
)
} else {
val computed by produceState(
initialValue = DayCellInfo("", false, null),
key1 = date,
key2 = lunarCache
) {
value = lunarCache.getOrCompute(date)
}
DayCellImpl(
date = date,
isCurrentMonth = isCurrentMonth,
isSelected = isSelected,
isToday = isToday,
shiftKind = shiftKind,
showLegalHoliday = showLegalHoliday,
holidayEdgeInfo = holidayEdgeInfo,
onClick = onClick,
modifier = modifier,
interactionSource = interactionSource,
lunarData = computed,
)
}
}
@Composable
private fun DayCellImpl(
date: LocalDate,
isCurrentMonth: Boolean,
isSelected: Boolean,
isToday: Boolean,
shiftKind: ShiftKind?,
showLegalHoliday: Boolean,
holidayEdgeInfo: HolidayEdgeInfo?,
onClick: () -> Unit,
modifier: Modifier,
interactionSource: MutableInteractionSource,
lunarData: DayCellInfo,
) {
val annotationText = lunarData.annotationText
val isAnnotationHighlight = lunarData.isAnnotationHighlight
val holidayBadge = lunarData.holidayBadge