Jetpack Compose LazyColumn列表展开项滚动后状态保留问题咨询
问题1解答
LazyColumn 本身是基于项复用的懒加载机制实现的,当列表项滚出可视区域后,对应的Greeting组件会直接从组合树中被销毁回收。你在Greeting中用remember存储的expanded状态是和组件本身的生命周期绑定的,组件销毁时状态也会同步被清除。等你滚回第一项时,Greeting会重新执行组合逻辑,remember会重新初始化expanded为默认值false,所以展开状态就恢复成初始的未展开状态了。
问题2解答
有两种常用的实现方案:
- 方案1:状态提升,把所有列表项的展开状态提到
LazyColumn的上层维护,比如在父组件中用remember存储一个Map<String, Boolean>或者Boolean类型的列表,对应每个项的展开状态,点击按钮时修改上层的状态值,Greeting组件的expanded直接从上层状态读取即可。上层组件不会因为列表滚动销毁,所以状态不会丢失。 - 方案2:将
Greeting中存储expanded的remember替换为rememberSaveable,也就是你给出的Code C的写法,即可直接实现状态保留。
问题3解答
普通Column加垂直滚动的逻辑和LazyColumn完全不同:Column会一次性把所有子项全部加入组合树,就算子项滚出可视区域也不会被销毁,只是暂时不在屏幕上渲染而已。所以Greeting组件的生命周期全程存在,用remember存储的expanded状态自然也不会被清除,滚回来的时候状态就还保留着。
注意:这种实现只适合少量列表项的场景,如果项的数量很大(比如上百上千个),一次性组合所有子项会带来非常大的性能开销,甚至出现卡顿。
问题4解答
你对rememberSaveable的认知有偏差,它的能力并不局限于配置变更场景。rememberSaveable存储状态时,会根据自身在组合树中的位置、传入的参数(这里你Greeting的name参数是唯一的,会作为状态存储的Key的一部分),将状态保存在全局的SavedStateRegistry中。
当列表项滚出屏幕Greeting被销毁时,对应Key的状态并不会被清除,等该项重新滚回可视区域、Greeting重新组合时,rememberSaveable会先去SavedStateRegistry中查找对应Key的历史状态,找到就直接复用之前的值,不会用你传入的默认值false,所以展开状态可以正常保留。
内容的提问来源于stack exchange,提问作者HelloCW

