ViewPager2无法动态添加或删除片段。

10
在Viewpager2中,对于特定索引添加/移除片段会导致意外的行为。这在ViewPager中是不可能的,但在Viewpager2中却被期望能够实现。它会导致重复的片段和不同步的TabLayout这里有一个演示项目可以重现此问题。有一个切换按钮,可以移除一个片段并将其重新附加到特定索引处。在这种情况下,附加的片段应该是绿色的,但它是蓝色的,并且出现了两个蓝色片段。

以下是我的适配器的样子:

class ViewPager2Adapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
    val fragmentList: MutableList<FragmentName> = mutableListOf()

    override fun getItemCount(): Int {
        return fragmentList.size
    }

    override fun createFragment(position: Int): Fragment {
        return when (fragmentList[position]) {
            FragmentName.WHITE -> WhiteFragment()
            FragmentName.RED -> RedFragment()
            FragmentName.GREEN -> GreenFragment()
            FragmentName.BLUE -> BlueFragment()
        }
    }

    fun add(fragment: FragmentName) {
        fragmentList.add(fragment)
        notifyDataSetChanged()
    }

    fun add(index: Int, fragment: FragmentName) {
        fragmentList.add(index, fragment)
        notifyDataSetChanged()
    }

    fun remove(index: Int) {
        fragmentList.removeAt(index)
        notifyDataSetChanged()
    }

    fun remove(name: FragmentName) {
        fragmentList.remove(name)
        notifyDataSetChanged()
    }

    enum class FragmentName {
        WHITE,
        RED,
        GREEN,
        BLUE
    }
}

我也向谷歌提交了一个漏洞报告

2个回答

12

原来,如果你在 ViewPager2 中使用了可变集合,那么你需要覆盖这两个方法。

override fun getItemId(position: Int): Long {
        return fragmentList[position].ordinal.toLong()
    }

    override fun containsItem(itemId: Long): Boolean {
        val fragment = FragmentName.values()[itemId.toInt()]
        return fragmentList.contains(fragment)
    }

在我的当前适配器中添加这两个可以修复问题


你在这个更改中是否遇到了片段在方向/配置更改时未重新附加的问题? - ScruffyFox
@ScruffyFox 我不记得测试过配置更改的情况,但这不应该是个问题,因为适配器会被重新创建,所有片段也会随之重新创建。请确保始终在createFragment中创建新的片段。 - Abhishek Bansal
当我从ViewPager中移除片段时,相关的ViewModel为null(与片段相同的生命周期),并且它不能正确更新。你如何处理这种情况? - Funnycuni
也许将 ViewModel 与父 Activity 关联会更好?另外,参见:https://developer.android.com/topic/libraries/architecture/viewmodel#sharing - pallgeuer
@user2066728,这是同样的事情。只需将其转换为相应的Java语法即可。 - Abhishek Bansal
@user2066728,对于需要的人 - 我在这里找到了一个不错的Java示例链接 - Taha

1

这在我的端上覆盖了原有的设置,感觉很奇怪。

getItemId()
containsItem()

当使用三种类型的片段时,会出现不良行为

最终,我只需要一个简单的FragmentStateAdapter类,就像这样:

class AppFragmentAdapter(private val fragmentList: MutableList<Pair<String, Fragment>>, fragment: Fragment) : FragmentStateAdapter(fragment) {

//    private var pageIds = fragmentList.map { fragmentList.hashCode().toLong() }

    override fun getItemCount(): Int = fragmentList.size

    override fun createFragment(position: Int): Fragment {
        return fragmentList[position].second
    }

//    override fun getItemId(position: Int): Long = pageIds[position] // Make sure notifyDataSetChanged() works

//    override fun containsItem(itemId: Long): Boolean = pageIds.contains(itemId)

    fun getFragmentName(position: Int) = fragmentList[position].first

    fun addFragment(fragment: Pair<String, Fragment>) {
        fragmentList.add(fragment)
        notifyDataSetChanged()
    }

    fun removeFragment(position: Int) {
        fragmentList.removeAt(position)
        notifyDataSetChanged()
    }

}

太遗憾了,我在寻找如何使ViewPager2动态化的即时答案之前,没有先尝试我能想到的最简单的方法。许多在这里回答的人指出,在向ViewPager2添加或删除Fragment时需要重写getItemId()containsItem(),这让我头疼了将近两天。感觉被背叛了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接