nordiccalendar

A simple Material 3 native calendar app fitting my personal requirements
git clone git://archive.git.mtrnord.blog/MTRNord/nordiccalendar.git
Log | Files | Refs | README | LICENSE

commit 723dc296f9548b9ea4ef47c0b9448e3bb2142fc1
parent 1191a7a3717f362eae0eacb0e0c72f95d925cb76
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Wed, 30 Jul 2025 00:42:34 +0200

Improve rendering performance

Diffstat:
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt | 110++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 56 insertions(+), 54 deletions(-)

diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt @@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.key import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -189,20 +190,13 @@ fun DayView( navController: NavController, calendarViewModel: CalendarViewModel, ) { - val events = remember(calendarViewModel) { - calendarViewModel.events - }.collectAsState() - + val events = remember(calendarViewModel) { calendarViewModel.events }.collectAsState() val hourHeightDp = 64.dp val timeColumnWidth = 64.dp val density = LocalDensity.current val hourHeightPx = with(density) { hourHeightDp.toPx() } - val dayStart = remember(calendarViewModel) { - calendarViewModel.startMillis - }.collectAsState() - val dayEnd = remember(calendarViewModel) { - calendarViewModel.endMillis - }.collectAsState() + val dayStart = remember(calendarViewModel) { calendarViewModel.startMillis }.collectAsState() + val dayEnd = remember(calendarViewModel) { calendarViewModel.endMillis }.collectAsState() val appLocale = getCurrentAppLocale(LocalContext.current) val hourFormat = DateTimeFormatter.ofPattern("HH:mm", appLocale) var now by remember { mutableLongStateOf(System.currentTimeMillis()) } @@ -232,12 +226,13 @@ fun DayView( val maxWidthPx = with(density) { maxWidth.toPx() } val lineThickness = 4.dp val lineColor = MaterialTheme.colorScheme.error - // Stunden-Divider + + // Stundenraster (eigene Ebene, recomposed nur bei Größenänderung) Column( modifier = Modifier .fillMaxWidth() ) { - for (hour in 0..24) { // Bis einschließlich 24, um 00:00 am Folgetag anzuzeigen + for (hour in 0..24) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -255,53 +250,60 @@ fun DayView( } } - val eventColumns = assignColumns(events.value) - eventColumns.forEach { (event, col, maxColumns) -> - // Berechne die sichtbaren Grenzen für diesen Tag - val shownStart = maxOf(event.startTime, dayStart.value) - val shownEnd = minOf(event.endTime, dayEnd.value) - val startMinutes = ((shownStart - dayStart.value) / 60000f) - val endMinutes = ((shownEnd - dayStart.value) / 60000f) - val offsetY = ((startMinutes + 30f) / 60f) * hourHeightPx - val eventHeightPx = ((endMinutes - startMinutes) / 60f) * hourHeightPx - val columnWidthPx = - (maxWidthPx - with(density) { timeColumnWidth.toPx() }) / maxColumns - val offsetX = - (col * columnWidthPx).toInt() + with(density) { timeColumnWidth.toPx() }.toInt() - val minCardHeightDp = 60.dp - val isCompact = with(density) { eventHeightPx.toDp() } < minCardHeightDp + // Events (eigene Ebene, recomposed nur bei Events- oder Größenänderung) + val eventColumns = remember(events.value, maxWidthPx) { assignColumns(events.value) } + events.value.forEach { event -> + val triple = eventColumns.find { it.first.id == event.id } + if (triple != null) { + val (event, col, maxColumns) = triple + val shownStart = maxOf(event.startTime, dayStart.value) + val shownEnd = minOf(event.endTime, dayEnd.value) + val startMinutes = ((shownStart - dayStart.value) / 60000f) + val endMinutes = ((shownEnd - dayStart.value) / 60000f) + val offsetY = (startMinutes / 60f) * hourHeightPx + val eventHeightPx = ((endMinutes - startMinutes) / 60f) * hourHeightPx + val columnWidthPx = + (maxWidthPx - with(density) { timeColumnWidth.toPx() }) / maxColumns + val offsetX = + (col * columnWidthPx).toInt() + with(density) { timeColumnWidth.toPx() }.toInt() + val minCardHeightDp = 60.dp + val isCompact = with(density) { eventHeightPx.toDp() } < minCardHeightDp - // Rundung-Logik - val noTopCorners = event.startTime < dayStart.value - val noBottomCorners = event.endTime > dayEnd.value + val noTopCorners = event.startTime < dayStart.value + val noBottomCorners = event.endTime > dayEnd.value - // Zeit-Label-Logik - val showStartTimeAsMidnight = shownStart == dayStart.value - val showEndTimeAsMidnight = shownEnd == dayEnd.value + val showStartTimeAsMidnight = shownStart == dayStart.value + val showEndTimeAsMidnight = shownEnd == dayEnd.value - EventCard( - event = event, - isCompact = isCompact, - onClick = { - navController.navigate("eventDetails/${event.eventId}?tab=2") { - launchSingleTop = true - restoreState = true + key(event.id) { + Box( + modifier = Modifier + .offset { IntOffset(offsetX, offsetY.toInt()) } + .width(with(density) { columnWidthPx.toDp() }) + .height(with(density) { eventHeightPx.toDp() }) + ) { + EventCard( + event = event, + isCompact = isCompact, + onClick = { + navController.navigate("eventDetails/${event.eventId}?tab=2") { + launchSingleTop = true + restoreState = true + } + }, + noTopCorners = noTopCorners, + noBottomCorners = noBottomCorners, + showStartTimeAsMidnight = showStartTimeAsMidnight, + showEndTimeAsMidnight = showEndTimeAsMidnight, + eventStartOverride = shownStart, + eventEndOverride = shownEnd + ) } - }, - modifier = Modifier - .width(with(density) { columnWidthPx.toDp() }) - .defaultMinSize(minHeight = 24.dp) - .padding(end = 8.dp) - .offset { IntOffset(offsetX, offsetY.toInt()) } - .height(with(density) { eventHeightPx.toDp() }), - noTopCorners = noTopCorners, - noBottomCorners = noBottomCorners, - showStartTimeAsMidnight = showStartTimeAsMidnight, - showEndTimeAsMidnight = showEndTimeAsMidnight, - eventStartOverride = shownStart, - eventEndOverride = shownEnd - ) + } + } } + + // Jetzt-Linie (eigene Ebene) if (nowOffsetY >= 0f && nowOffsetY <= hourHeightPx * 24.5f) { Row( modifier = Modifier