这应该不会太困难,但由于某种原因,重组经历了两次,这使得添加仅第一次执行动画的条件变得困难。
一般来说,我想像这个例子那样做,但我改变了一点,但本质是一样的 - https://yasincamaz.medium.com/simple-item-animation-with-jetpack-composes-lazygrid -78316992af22
网格元素出现气泡效果是必要的
这是代码
private val dataSet: List<String> = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
private val data: List<String> = List(5) { dataSet }.flatten()
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Test_delete_itTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Gallery(
paddingValues = innerPadding,
uiConfig = { data }
)
}
}
}
}
}
@Composable
private fun Gallery(
paddingValues: PaddingValues,
uiConfig: () -> List<String>
) {
val config: List<String> = uiConfig()
val columns = 2
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
LazyVerticalGrid(
columns = GridCells.Fixed(columns),
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = {
items(config.size) { idx ->
val item: String = config[idx]
val (scale, alpha) = scaleAndAlpha(idx, columns)
MyItem(
modifier = Modifier.graphicsLayer(alpha = alpha, scaleX = scale, scaleY = scale),
text = item
)
}
}
)
}
}
@Composable
private fun MyItem(
modifier: Modifier = Modifier,
text: String
) {
Card(
modifier = modifier.height(150.dp),
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(8.dp),
colors = CardDefaults.cardColors(
containerColor = Color.Blue,
)
) {
Box(
modifier = Modifier
.weight(1f)
.height(150.dp)
.clip(RoundedCornerShape(16.dp))
) {
Text(
text = text,
color = Color.White
)
}
}
}
@Immutable
private enum class State { PLACING, PLACED }
@Immutable
data class ScaleAndAlphaArgs(
val fromScale: Float,
val toScale: Float,
val fromAlpha: Float,
val toAlpha: Float
)
@OptIn(ExperimentalTransitionApi::class)
@Composable
fun scaleAndAlpha(
args: ScaleAndAlphaArgs,
animation: FiniteAnimationSpec<Float>
): Pair<Float, Float> {
val transitionState = remember { MutableTransitionState(State.PLACING).apply { targetState = State.PLACED } }
val transition = rememberTransition(transitionState, label = "")
val alpha by transition.animateFloat(transitionSpec = { animation }, label = "") {
if (it == State.PLACING) args.fromAlpha else args.toAlpha
}
val scale by transition.animateFloat(transitionSpec = { animation }, label = "") {
if (it == State.PLACING) args.fromScale else args.toScale
}
return alpha to scale
}
val scaleAndAlpha: @Composable (idx: Int, columns: Int) -> Pair<Float, Float> = { idx, columns ->
scaleAndAlpha(
args = ScaleAndAlphaArgs(2f, 1f, 0f, 1f),
animation = tween(300, delayMillis = (idx / columns) * 100)
)
}
您可以尝试将Gallery其更改为此以跟踪该元素是否已向用户显示
@Composable
private fun Gallery(
paddingValues: PaddingValues,
uiConfig: () -> List<String>
) {
val config: List<String> = uiConfig()
val columns = 2
// Remember a set of already animated indices
val animatedIndices = remember { mutableSetOf<Int>() }
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
LazyVerticalGrid(
columns = GridCells.Fixed(columns),
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = {
items(config.size) { idx ->
val item: String = config[idx]
// Determine if the item should animate
val shouldAnimate = !animatedIndices.contains(idx)
// If it should animate, mark it as animated
if (shouldAnimate) {
animatedIndices.add(idx)
}
val (scale, alpha) = if (shouldAnimate) {
scaleAndAlpha(idx, columns)
} else {
1f to 1f // No animation
}
MyItem(
modifier = Modifier.graphicsLayer(alpha = alpha, scaleX = scale, scaleY = scale),
text = item
)
}
}
)
}
}
但问题是,这里每个元素都会调用两次重组 -items(config.size) { idx ->因此,该元素出现在屏幕上就好像没有动画一样
我在这里缺少什么?
在第一次重组项目 lambda 时,当 shouldAnimate 为 true 时,将调用scaleAndAlpha。这是一个 Compose 函数,它会在动画的每一帧上重新编译,并在每次重新组合时返回当前的比例和 alpha 值。为了确保 MyItem 相应更新,当比例和 alpha 更改时,会重新编译整个项目 lambda。
第二次重组是不可取的,因为 shouldAnimate 现在设置为 false 并且刚刚开始的动画被完全跳过。
一个简单的解决方案是将scaleAndAlpha 和MyItem 移动到一个单独的可组合元素中,以便其重新组合不依赖于shouldAnimate。
现在,由于动画而发生的重组受到限制
MyAnimatedItem,lambdaitemsIndexed不受影响,并且仅在元素滚动到范围之外并返回时才重新编译。只有这样,shouldAnimate它才会false按照预期安装在 中。