You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 12:45:03