ViewPager2使用FragmentStateAdapter动态移除替换Fragment不生效
问题原因
FragmentStateAdapter 内部通过唯一ID缓存Fragment实例,默认实现会直接把position作为itemId返回。你当前实现中position=1的位置虽然切换了nextFragment指向的实例,但itemId始终为1,Adapter会判定该位置的Fragment没有变化,直接复用缓存里的旧F2实例,不会调用createFragment()生成新的F3,自然不会显示新页面。
修复方案
- 重写
getItemId()为每个不同的Fragment返回唯一稳定ID,让Adapter能识别到同位置下Fragment实例已经变更 - 重写
containsItem()匹配ID校验逻辑,保证Adapter能正确移除失效的旧Fragment缓存 - 每次更新要展示的Fragment类型后调用
notifyDataSetChanged()主动触发刷新 - 不要在Adapter中手动持有Fragment实例引用,实例创建逻辑全部放在
createFragment()中交给FragmentManager管理,避免状态错乱
修正后的PagerAdapter代码
import androidx.recyclerview.widget.RecyclerView class PagerAdapter( fragment: PagerFragment, ) : FragmentStateAdapter(fragment) { // 标记当前要展示的二级页面类型:-1=无二级页,1=F2,2=F3 private var currentSecondPageType: Int = -1 private val firstPageItemId = 0L override fun getItemCount(): Int { // 没有二级页的时候只展示F1 return if (currentSecondPageType == -1) 1 else 2 } override fun createFragment(position: Int): Fragment { return when (position) { 0 -> F1() 1 -> when (currentSecondPageType) { 1 -> F2() 2 -> F3() else -> DummyDialogFragment() } else -> throw IndexOutOfBoundsException("Invalid position: $position") } } override fun getItemId(position: Int): Long { return when (position) { 0 -> firstPageItemId // 不同二级页返回不同ID,保证Adapter能识别到实例变更 1 -> currentSecondPageType.toLong() else -> RecyclerView.NO_ID } } override fun containsItem(itemId: Long): Boolean { return itemId == firstPageItemId || itemId == currentSecondPageType.toLong() } fun setNextFragment(page: Int) { currentSecondPageType = page // 通知Adapter数据集变更,触发旧缓存清理和新Fragment创建 notifyDataSetChanged() } }
修正后的跳转方法
fun navigateTo(page: Int) { adapter.setNextFragment(page) binding?.viewPager?.currentItem = if (page == 0) 0 else 1 }
注意:你之前设置的
binding?.viewPager?.isSaveEnabled = false可以删掉,正确实现ID逻辑后ViewPager2的状态保存机制可以正常工作,不需要关闭。
内容的提问来源于stack exchange,提问作者mefahimrahman
相关产品推荐
相关产品推荐

