mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
performance: reduce recompositons in PersonCard and SeasonCard (#1184)
<!-- By submitting this pull request, you acknowledge that you have read the [contributing guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md, including the AI/LLM policy, and [developer's guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md) --> ## Description <!-- Describe the changes in detail --> related docs: https://developer.android.com/develop/ui/compose/performance/bestpractices#defer-reads Removed setting padding with animated value, instead moved to lambda offset and layout modifiers to increase performance and reduce recompositions when focusing those cards Split from #1152 ### Related issues <!-- If this is a new feature or a change, there must be a discussion in an issue first, reference the issue here --> <!-- If fixing a bug, reference the bug issue here, or describe the bug, including steps to reproduce --> ### Testing <!-- Describe how this change was tested and on what device(s) --> Tested on a emulato ## Screenshots <!-- Please include screenshots if the PR alters any UI elements --> |Component | Pre | Post | |---|--------|--------| |Person Card|<img width="404" height="366" alt="Screenshot 2026-04-02 at 08 19 18" src="https://github.com/user-attachments/assets/73b38c75-040b-401e-88d6-4bfc9ffc0cc9" /> | <img width="407" height="346" alt="Screenshot 2026-04-02 at 08 22 06" src="https://github.com/user-attachments/assets/add473f7-dea6-4e33-97b8-6954149a81e2" /> | |Season Card| <img width="404" height="341" alt="Screenshot 2026-04-02 at 08 18 31" src="https://github.com/user-attachments/assets/eec594ee-6701-4f99-8fb3-339ed8559ec4" /> |<img width="408" height="299" alt="Screenshot 2026-04-02 at 08 24 06" src="https://github.com/user-attachments/assets/8de445a6-dcb8-4c7e-a5f6-a25bf822c822" /> | ## AI or LLM usage <!-- If you used any AI or LLM assistance, please list where in the code and how you tested it --> Gemini in AS was used for repetitive code completion
This commit is contained in:
parent
44d1ac484f
commit
5ab4bb712b
2 changed files with 40 additions and 12 deletions
|
|
@ -11,6 +11,7 @@ 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.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
|
|
@ -25,9 +26,11 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.Border
|
||||
|
|
@ -80,7 +83,7 @@ fun PersonCard(
|
|||
) {
|
||||
val hideOverlayDelay = 1_000L
|
||||
|
||||
val focused = interactionSource.collectIsFocusedAsState().value
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
var focusedAfterDelay by remember { mutableStateOf(false) }
|
||||
|
||||
if (focused) {
|
||||
|
|
@ -95,10 +98,13 @@ fun PersonCard(
|
|||
} else {
|
||||
focusedAfterDelay = false
|
||||
}
|
||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
||||
|
||||
// Do not use `by` here, this way we are Defer reads and recompositions to only when modifier calculates
|
||||
val spaceBetweenState = animateDpAsState(if (focused) 12.dp else 4.dp, label = "spaceBetween")
|
||||
val spaceBelowState = animateDpAsState(if (focused) 4.dp else 12.dp, label = "spaceBelow")
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp), // Fixed base spacing
|
||||
modifier = modifier,
|
||||
) {
|
||||
Card(
|
||||
|
|
@ -168,8 +174,16 @@ fun PersonCard(
|
|||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(bottom = spaceBelow)
|
||||
.fillMaxWidth(),
|
||||
// Optimization: move animation reads to layout/draw phase
|
||||
.offset {
|
||||
IntOffset(0, (spaceBetweenState.value - 4.dp).roundToPx())
|
||||
}.layout { measurable, constraints ->
|
||||
val paddingPx = spaceBelowState.value.roundToPx()
|
||||
val placeable = measurable.measure(constraints)
|
||||
layout(placeable.width, placeable.height + paddingPx) {
|
||||
placeable.placeRelative(0, 0)
|
||||
}
|
||||
}.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = name ?: "",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ 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
|
||||
|
|
@ -18,10 +19,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.layout
|
||||
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
|
||||
|
|
@ -52,7 +55,7 @@ fun SeasonCard(
|
|||
val imageUrlService = LocalImageUrlService.current
|
||||
val density = LocalDensity.current
|
||||
val imageUrl =
|
||||
remember(item, imageHeight, imageWidth) {
|
||||
remember(item, imageHeight, imageWidth, density) {
|
||||
if (item != null) {
|
||||
val fillHeight =
|
||||
if (imageHeight != Dp.Unspecified) {
|
||||
|
|
@ -125,14 +128,17 @@ fun SeasonCard(
|
|||
aspectRatio: Float = AspectRatios.TALL,
|
||||
) {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
||||
// 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 focusedAfterDelay by rememberFocusedAfterDelay(interactionSource)
|
||||
val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN)
|
||||
val width = imageHeight * aspectRationToUse
|
||||
val height = imageWidth * (1f / aspectRationToUse)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp), // Fixed base spacing
|
||||
modifier = modifier.size(width, height),
|
||||
) {
|
||||
Card(
|
||||
|
|
@ -173,8 +179,16 @@ fun SeasonCard(
|
|||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(bottom = spaceBelow)
|
||||
.fillMaxWidth(),
|
||||
// Optimization: move animation reads to layout/draw phase
|
||||
.offset {
|
||||
IntOffset(0, (spaceBetween.value - 4.dp).roundToPx())
|
||||
}.layout { measurable, constraints ->
|
||||
val paddingPx = spaceBelow.value.roundToPx()
|
||||
val placeable = measurable.measure(constraints)
|
||||
layout(placeable.width, placeable.height + paddingPx) {
|
||||
placeable.placeRelative(0, 0)
|
||||
}
|
||||
}.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = title ?: "",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue