由于某种原因,发生了 2 次重新组合,导致无法为项目设置条件来检查它是否已呈现给用户。
我想为 LazyGrid 制作这样的动画(我尝试稍微优化一下我的代码,但含义相同) - https://yasinkacmaz.medium.com/simple-item-animation-with-jetpack-组成-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)
)
}
我第一次尝试添加一个条件:
@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 ->
这使得条件变得毫无用处。
我在这里缺少什么?
在
items
lambda 的第一个组合中,当 shouldAnimate
为 true
时,调用 scaleAndAlpha
。这是一个在动画的每一帧上重新组合的组合函数。它还会返回每次重组时 scale
和 alpha
的当前值。为了使 MyItem
能够相应更新,当 items
和 scale
发生变化时,整个 alpha
lambda 都会被重构。
这是您不希望拥有的第二个合成,因为现在
shouldAnimate
设置为 false
并且刚刚开始的动画被完全跳过。
一个简单的修复方法是将
scaleAndAlpha
和 MyItem
提取到专用的可组合项中,以便其重组独立于 shouldAnimate
:
@Composable
private fun MyAnimatedItem(
shouldAnimate: Boolean,
idx: Int,
columns: Int,
item: String,
) {
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,
)
}
简单地这样调用(另外我将其简化为使用
itemsIndexed
而不是items
):
itemsIndexed(config) { idx, item ->
// Determine if the item should animate
val shouldAnimate = !animatedIndices.contains(idx)
// If it should animate, mark it as animated
if (shouldAnimate) {
animatedIndices.add(idx)
}
MyAnimatedItem(shouldAnimate, idx, columns, item)
}
现在,由于动画而发生的重组仅限于
MyAnimatedItem
,itemsIndexed
lambda 不受影响,并且仅在项目滚出视口并再次滚入时才会重组。并且只有 then shouldAnimate
按预期设置为 false
。