ViewPager2 无法动态添加删除片段

Posted

技术标签:

【中文标题】ViewPager2 无法动态添加删除片段【英文标题】:ViewPager2 not able to dynamically add remove fragment 【发布时间】:2020-08-07 21:31:45 【问题描述】:

在索引处删除/添加片段会导致 Viewpager2 出现意外行为。 ViewPager 无法做到这一点,但预计可以与 Viewpager2 一起使用。它会导致重复片段和不同步TabLayout。 Here is a demo project 重现了这个问题。有一个切换按钮可以删除片段并将其重新附加到特定索引处。在这种情况下,附加的片段应该是绿色的,但它是蓝色的,并且不知何故有 2 个蓝色片段。

这是我的适配器的外观

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
    

我也向谷歌提交了bug

【问题讨论】:

【参考方案1】:

事实证明,如果您在 ViewPager2 中使用 mutable 集合,则需要覆盖这两个方法

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)
    

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

【讨论】:

救世主!过去 2 小时一直在尝试解决此问题 您是否遇到过因此更改而无法在方向/配置更改时重新附加片段的问题? @ScruffyFox 我不记得测试过配置更改场景,但这应该不是问题,因为重新创建了适配器并且所有片段都随之重新创建。确保始终在createFragment 中创建新片段。 当我从 ViewPager 中删除片段时,关联的 ViewModel 为空(与片段相同的生命周期)并且无法正确更新。您如何处理这种情况? 也许将 ViewModel 与父活动相关联?另外:developer.android.com/topic/libraries/architecture/…【参考方案2】:

这对我来说很奇怪

getItemId()
containsItem()

在使用 3 种 Fragment 时只会给出undesirable behavior。

最后我只需要一个像这样的简单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(),这让人头疼了将近 2 天。感觉被背叛了。

【讨论】:

以上是关于ViewPager2 无法动态添加删除片段的主要内容,如果未能解决你的问题,请参考以下文章

Extjs 无法在表单面板中动态添加/删除字段

使用 FragmentStatePagerAdapter 在 ViewPager 的两侧动态添加片段

删除 viewPager2 中的片段使用 FragmentStateAdapter,但仍显示

在 Android 中加载动态项目时无法将页脚视图添加到列表视图

Html 使用 javascript 动态添加和删除行

在 ListView 中动态添加 LinearLayout