Demo animated content scale

This commit is contained in:
Jolanda Verhoef 2024-12-13 14:20:05 +00:00
parent 4c31f4a84b
commit 1beaa553ea

View File

@ -21,6 +21,9 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@ -30,15 +33,20 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.ScaleFactor
import androidx.compose.ui.layout.lerp
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.LifecycleResumeEffect
@ -124,7 +132,7 @@ private fun MediaPlayerScreen(player: Player, modifier: Modifier = Modifier) {
player = player,
surfaceType = SURFACE_TYPE_SURFACE_VIEW,
modifier = Modifier.noRippleClickable { showControls = !showControls },
contentScale = contentScales[currentContentScaleIndex].second
contentScale = contentScales[currentContentScaleIndex].second.interpolate()
)
if (!renderingState.renderedFirstFrame) {
// hide the surface that is being prepared behind a scrim
@ -147,3 +155,87 @@ private fun MediaPlayerScreen(player: Player, modifier: Modifier = Modifier) {
}
}
}
@Composable
private fun ContentScale.interpolate(): ContentScale {
var interpolation by remember {
mutableFloatStateOf(1f)
}
var previousScale by remember {
mutableStateOf(this)
}
val currentScale by remember {
mutableStateOf(this)
}.apply {
if (value != this@interpolate) previousScale = when {
interpolation == 1f -> value
else -> CapturedContentScale(
capturedInterpolation = interpolation,
previousScale = previousScale,
currentScale = value
)
}.also { interpolation = 0f }
value = this@interpolate
}
LaunchedEffect(currentScale) {
animate(
initialValue = 0f,
targetValue = 1f,
animationSpec = spring(
stiffness = Spring.StiffnessMedium
),
block = { progress, _ ->
interpolation = progress
},
)
}
return remember {
object : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size
): ScaleFactor {
val start = previousScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
)
val stop = currentScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
)
return if (start == stop) stop
else lerp(
start = start,
stop = stop,
fraction = interpolation
)
}
}
}
}
private class CapturedContentScale(
private val capturedInterpolation: Float,
private val previousScale: ContentScale,
private val currentScale: ContentScale,
) : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size
): ScaleFactor = lerp(
start = previousScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
),
stop = currentScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
),
fraction = capturedInterpolation
)
}