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
有一个Button
把animationState.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,
而且我真的想不出如何避免它。
我使用 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
有一个Button
把animationState.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,
而且我真的想不出如何避免它。