Jetpack Compose中onSizeChanged每次重组都被触发的问题求助
问题根因
你遇到的重复回调问题核心原因是:
- 每次重组时直接创建的
onSizeChanged修饰符实例不稳定:onSizeChanged传入的lambda默认每次重组都会生成新实例,导致修饰符被判定为变更,触发重新附着,其内部缓存的上一次尺寸会被重置为初始值,测量完成后哪怕尺寸未发生真实变更,也会触发一次回调。 - 你的
composed用法存在问题:未在composed内部缓存上一次尺寸,也没有稳定回调实例,每次composed块执行仍会创建新的onSizeChanged修饰符,无法解决重置问题。
解决方案
封装稳定版的尺寸变更修饰符,在composed内部通过remember持久化上一次尺寸记录,仅在尺寸真实变更时触发回调:
fun Modifier.stableOnSizeChanged( onSizeChanged: (IntSize) -> Unit ) = composed { // 持久化上一次的尺寸,修饰符不被卸载就不会重置 var lastSize by remember { mutableStateOf<IntSize?>(null) } Modifier.onSizeChanged { newSize -> if (lastSize != newSize) { lastSize = newSize onSizeChanged(newSize) } } }
修正后的示例代码
var counter by remember { mutableStateOf(0) } LaunchedEffect(key1 = counter) { delay(1000) counter++ } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( modifier = Modifier.stableOnSizeChanged { // 仅尺寸真实变更时会执行此处逻辑 println("当前组件尺寸:$it") }, text = "Hello, $counter!", color = Color.Black ) }
注意事项
- 如果回调逻辑需要捕获重组作用域内的可变状态,需要将对应状态作为
remember的key更新回调,例如需要用到counter时按如下写法调整:
fun Modifier.stableOnSizeChanged( key1: Any? = null, onSizeChanged: (IntSize) -> Unit ) = composed { var lastSize by remember { mutableStateOf<IntSize?>(null) } // 传入key更新回调逻辑 val rememberedCallback = remember(key1) { onSizeChanged } Modifier.onSizeChanged { newSize -> if (lastSize != newSize) { lastSize = newSize rememberedCallback(newSize) } } } // 使用时传入依赖的状态 .stableOnSizeChanged(key1 = counter) { println("counter为$counter 时的尺寸:$it") }
- 原生
onSizeChanged本身已实现尺寸对比逻辑,重复回调的本质是修饰符重附着导致的缓存重置,不需要修改原生源码逻辑。
内容的提问来源于stack exchange,提问作者Mahmoud Afarideh
相关产品推荐
相关产品推荐

