问题是Context
not成为一个stable https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8 (@Stable
) 类型。的 lambda/回调KeyPad
正在更新一个状态,紧接着是一个使用不稳定状态的组件Context
,这导致onClickLambda
to be 重新创造 https://stackoverflow.com/questions/72633554/why-does-a-call-to-a-logic-method-on-a-viewmodel-with-stateflow-as-a-property-ca(你可以看到它的hashcode每次单击按钮时都会更改),从而使Keypad
可组合的not 可跳过 https://chris.banes.me/posts/composable-metrics/.
您可以考虑四种方法来处理您的问题。我还对代码进行了一些更改,删除了本地函数,并将所有内容直接放入 lambda/回调中,以使所有内容更小。
对于前两个,首先创建一个像这样的通用包装类。
@Stable
data class StableWrapper<T>(val value: T)
将上下文包装在 @Stable 包装器中
使用通用包装类,您可以考虑包装上下文并像这样使用它
@Composable
fun Main() {
Log.e("Composable", "Composed Main")
var text by remember { mutableStateOf("") }
val context = LocalContext.current
val contextStableWrapper = StableWrapper(context)
Column {
Text(text)
Keypad {
text = it.toString()
Toast.makeText(contextStableWrapper.value, "Toast", Toast.LENGTH_SHORT).show()
}
}
}
将 Toast 包裹在 @Stable 包装器中
Toast也是一种不稳定的类型,所以你必须用第二种方法使其“稳定”。
请注意,这仅适用于您的 Toast 消息不会更改的情况。
将它们举到你的上方Main
您将在其中创建静态消息 Toast 的实例并将其放入稳定的包装器中
val toastWrapper = StableWrapper(
Toast.makeText(LocalContext.current, "Toast", Toast.LENGTH_SHORT)
)
Main(toastWrapper = toastWrapper)
你的主要可组合项将如下所示
@Composable
fun Main(toastWrapper: StableWrapper<Toast>) {
Log.e("Composable", "Composed Main")
var text by remember { mutableStateOf("") }
Column {
Text(text)
Keypad {
text = it.toString()
toastWrapper.value.show()
}
}
}
remember{…}
上下文
(我可能期望在这里进行一些修正),我认为这被称为“记忆值(Context
) 里面remember{…}
“,这看起来类似于延迟读取 https://medium.com/androiddevelopers/jetpack-compose-debugging-recomposition-bfcf4a6f8d37.
@Composable
fun Main() {
Log.e("Composable", "Composed Main")
var text by remember { mutableStateOf("") }
val context = LocalContext.current
val rememberedContext = remember { { context } }
Column {
Text(text)
Keypad {
text = it.toString()
Toast.makeText(rememberedContext(), "Toast", Toast.LENGTH_SHORT).show()
}
}
}
使用副作用
您可以利用撰写副作用 https://developer.android.com/jetpack/compose/side-effects然后把吐司放进去。
Here, SideEffect
将执行每个重组后。
SideEffect {
if (text.isNotEmpty()) {
Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
}
}
或者你可以利用LaunchedEffect
使用text
作为它的关键,所以在成功的重新组合时,当text
与之前的值不同(无效)的变化,LaunchedEffect
将重新执行并再次显示 toast
LaunchedEffect(key1 = text) {
if (text.isNotEmpty()) {
Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
}
}
将打印替换为日志语句,这是单击按钮时任何方法的输出
E/Composable: Composed Main // first launch of screen
E/Composable: Composed Keypad // first launch of screen
// succeeding clicks
E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main
我唯一不确定的部分是第一种方法,即使 Toast 不是基于第二种方法的稳定类型,只需在第一种方法中将上下文包装在稳定包装器中就足以满足Keypad
可组合以被跳过。