From 01bd3249faa750f5e8a56960351742f51821138a Mon Sep 17 00:00:00 2001 From: Damontecres Date: Mon, 8 Jun 2026 17:23:09 -0400 Subject: [PATCH] Fixes two row bouncing UI issues (#1543) ## Description Fixes row bounce using `SeasonCard` when are two exactly two items. This was done by calculating the y-offset in the `layout` modifier instead of `offset`. This has the side effect of probably being very slightly more efficient since only 1 animation state is needed now. Fixes row bounce on the second row of the home page if the first row is empty and not shown. ### Related issues Fixes #1528 Fixes #1271 ### Testing Emulator in different situations. Also checked the layout inspector to make sure the optimizations from #1184 weren't affected ## Screenshots N/A ## AI or LLM usage None --- .../wholphin/ui/cards/SeasonCard.kt | 24 ++++++++++--------- .../damontecres/wholphin/ui/main/HomePage.kt | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SeasonCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SeasonCard.kt index 962ab727..5caa45bf 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SeasonCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/SeasonCard.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -24,7 +23,6 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.tv.material3.Card import androidx.tv.material3.CardDefaults @@ -104,6 +102,9 @@ fun SeasonCard( ) } +private val spaceBetweenFocused = 4.dp +private val spaceBetweenUnfocused = 12.dp + /** * A Card for a TV Show Season, but can generically show most items */ @@ -129,8 +130,11 @@ fun SeasonCard( ) { val focused by interactionSource.collectIsFocusedAsState() // Do not use `by` here, this way we are Defer reads and recompositions to only when modifier calculates - val spaceBetween = animateDpAsState(if (focused) 12.dp else 4.dp, label = "spaceBetween") - val spaceBelow = animateDpAsState(if (focused) 4.dp else 12.dp, label = "spaceBelow") + val spaceBetween = + animateDpAsState( + if (focused) spaceBetweenFocused else spaceBetweenUnfocused, + label = "spaceBetween", + ) val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource) val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN) @@ -179,14 +183,12 @@ fun SeasonCard( verticalArrangement = Arrangement.spacedBy(0.dp), modifier = Modifier - // Optimization: move animation reads to layout/draw phase - .offset { - IntOffset(0, (spaceBetween.value - 4.dp).roundToPx()) - }.layout { measurable, constraints -> - val paddingPx = spaceBelow.value.roundToPx() + .layout { measurable, constraints -> + val topPaddingPx = (spaceBetweenUnfocused - spaceBetween.value).roundToPx() + val bottomPaddingPx = spaceBetween.value.roundToPx() val placeable = measurable.measure(constraints) - layout(placeable.width, placeable.height + paddingPx) { - placeable.placeRelative(0, 0) + layout(placeable.width, placeable.height + bottomPaddingPx + topPaddingPx) { + placeable.placeRelative(0, topPaddingPx) } }.fillMaxWidth(), ) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index db76fb3e..afaaac56 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -374,7 +374,7 @@ fun HomePageContent( itemsIndexed(homeRows) { rowIndex, row -> val rowModifier = Modifier - .animateItem() + .animateItem(placementSpec = null) .padding(bottom = 8.dp) CompositionLocalProvider( LocalBringIntoViewSpec provides defaultBringIntoViewSpec,