Jetpack Compose MutableTransitionState currentState 持有错误的价值

Jetpack Compose MutableTransitionState currentState holds wrong value

我使用 AnimatedVisibility 来制作从 LazyColumn 中删除项目的动画,并使用 MutableTransitionState 捕捉动画的结尾以从 LazyColumn 的列表中删除该项目.

我为此编写了一个方便的扩展函数:

@ExperimentalTransitionApi
fun MutableTransitionState<Boolean>.transitionState(): TransitionState =
         if (this.isIdle && this.currentState)  TransitionState.Visible
    else if (this.isIdle && !this.currentState) TransitionState.Invisible
    else if (!this.isIdle && this.currentState) TransitionState.Disappearing
    else TransitionState.Appearing


enum class TransitionState(private val whatever: Int) {
    Visible(1),
    Appearing(2),
    Invisible(-1),
    Disappearing(-2)
}

从某种意义上说它是正确的 returns 正确的值(已测试),但 currentState 似乎只是 false 一开始,所以我无法捕捉到唯一的事件我对 - Invisible.

感兴趣

这是我的 LazyColumn:

val items by viewModel.itemsFlow.collectAsState()

LazyColumn(
    verticalArrangement = Arrangement.spacedBy(10.dp),
    contentPadding = PaddingValues(horizontal = 15.dp, vertical = 15.dp)
) {
    items(items) { item ->
        val animationState = remember {
            MutableTransitionState(false) // The only time currentState is false!
        }.apply { targetState = true }
        AnimatedVisibility(
            visibleState = animationState,
            enter = slideInVertically() + fadeIn(),
            exit = slideOutHorizontally(
                targetOffsetX = { it*2 },
                animationSpec = tween(
                    durationMillis = 700
                )
            )
        ) {
            ItemCard(
                item = item,
                viewModel = viewModel,
                animationState = animationState
            )
        }
    }
}

我的ItemCard有一个ButtonanimationState.tagetValue改成了false,状态记录在卡里面:

Card {
    Log.v("ANIMATION", "View ${item.name} is now ${animationState.transitionState().name}")
    Log.v("ANIMATION", "View ${item.name} has values: isIdle = ${animationState.isIdle}, currentState = ${animationState.currentState}")
        /*...*/
        Button(
            /*...*/
            onClick = {
                animationState.targetState = false
            }
        ) {/*...*/}
 }

不幸的是,我的日志是这样的:

V/ANIMATION: View name is now Appearing
V/ANIMATION: View name has values: isIdle = false, currentState = false
V/ANIMATION: View name is now Appearing
V/ANIMATION: View name has values: isIdle = false, currentState = false
V/ANIMATION: View name is now Visible
V/ANIMATION: View name has values: isIdle = true, currentState = true
V/ANIMATION: View name is now Visible
V/ANIMATION: View name has values: isIdle = true, currentState = true
// After I click the button:
V/ANIMATION: View name is now Disappearing
V/ANIMATION: View name has values: isIdle = false, currentState = true
V/ANIMATION: View name is now Disappearing
V/ANIMATION: View name has values: isIdle = false, currentState = true
V/ANIMATION: View name is now Disappearing
V/ANIMATION: View name has values: isIdle = false, currentState = true

那么隐形状态在哪里,即false currentState?我是不是做错了什么?

每次改变状态,都会触发相关的视图重组。并且在每次重组时将 animationState 设置为 true .apply { targetState = true }.

很可能你想在开始时显示动画,然后你需要使用LaunchedEffect:它只会在视图出现时调用一次。

val animationState = remember {
    MutableTransitionState(false)
}
LaunchedEffect(Unit) {
    animationState.targetState = true
}

阅读 Thinking in Compose and about state in Compose in state documentation 中有关重组的更多信息。

经过一些测试...

我在一个单独的项目上测试了这种类型的动画,发现 AnimatedVisibility 内容在 animationState 发出其最终值之前被处理掉,所以你不可能从内部捕捉到它您正在设置动画的视图。

按照@Philip Dukhov 的建议,我的 LazyColumn 项目现在是这样的:

            val animationState by remember {
                mutableStateOf(MutableTransitionState(false))
            }
            LaunchedEffect(Unit) {
                animationState.targetState = true
            }
            when(animationState.transitionState()) {
                TransitionState.Invisible -> viewModel.deleteInvisibleItems()
                else -> {}
            }
            Log.v("ANIMATION", "View ${item.name} is now ${animationState.transitionState().name}")

这完美地捕获了发出的值。

但是,它存在严重的性能问题,logcat 表明:

I/Choreographer: Skipped 39 frames!  The application may be doing too much work on its main thread.
I/OpenGLRenderer: Davey! duration=992ms; Flags=0, IntendedVsync=189347804710717, Vsync=189348454710691, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=189348467819123, AnimationStart=189348467823342, PerformTraversalsStart=189348779946237, DrawStart=189348780275977, SyncQueued=189348792306499, SyncStart=189348792749051, IssueDrawCommandsStart=189348793026916, SwapBuffers=189348794352801, FrameCompleted=189348797854572, DequeueBufferDuration=181354, QueueBufferDuration=287916, GpuCompleted=188965731175579, 

而且我真的想不出如何避免它。