【问题标题】:PagerAdapter.notifyDataSetChanged does not refresh fragmentsPagerAdapter.notifyDataSetChanged 不刷新片段
【发布时间】:2015-01-23 18:48:40
【问题描述】:

我的Activity 包含一个ViewPager,它是自定义的Adapter,它扩展了FragmentStatePagerAdapterViewPager 包含 3 个片段

从 ViewPager 中删除 Fragments 的代码

MainActivity

public void deleteElement(){
    final int currentItem = mViewPager.getCurrentItem();
    mPagerAdapter.removeItem(currentItem);
    mPagerAdapter.notifyDataSetChanged();
}

CustomPagerAdapter

private ArrayList<Item> data;
public void removeItem(int index){
        data.remove(index);
}

删除中间Fragment(索引1)时:

  • 来自data,我正在删除正确的item
  • 问题是我(我猜)Fragment 3 在notifyDataSetChanged 之后被删除,当前的Fragment 仍然是用户看到的片段,并且正在加载的数据来自SavedInstance

所以最终结果是用户仍然看到他试图删除的相同Fragment,这对他来说有点糟糕。

想法?

***** 编辑 ******

似乎ViewPager2 Features 可能会解决这个问题以及许多其他问题

【问题讨论】:

  • 我建议切换到处理此问题的my ArrayPagerAdapterFragmentStatePagerAdapter 在这方面存在问题,我并不感到非常惊讶,这就是为什么我单独推出一些东西。
  • 试试this
  • 好简单的解决方案:stackoverflow.com/a/35450575/2162226
  • @gnB 这不是解决这个问题的方法

标签: android android-viewpager android-pageradapter


【解决方案1】:

尝试添加这个:

private class MyPagerAdapter extends FragmentStatePagerAdapter {

//... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

}

【讨论】:

  • 但它会导致 android 重新启动我所有的片段,这有点没有意义?
  • 是的,here 其他选项
  • 我知道这两个选项,并且在提问之前我看到了这些帖子。问题是我不是在更新视图而是删除视图。我不能确定哪个视图会被删除,因此按标签调用片段并不安全(也许它已经被破坏了?我应该更新所有其他片段吗?)
  • 感谢它为我所做的工作:扩展 FragmentStatePagerAdapter
【解决方案2】:

你别无选择,只能实现PagerAdapter#getItemPosition。查看源代码,这正是 ViewPager 确定如何更新其位置、添加新片段或在notifyDataSetChanged 之后丢弃死片段的方式。应该不会太难,你只需要访问传递的片段的状态,你还需要访问你的活动中的主状态来保存你的排序。

还需要您实现 getItemId 为每个片段返回一致的 ID,因为它们可以重新排序。

这就是我在照片的 ViewPager 中处理它的方式。

    private inner class PhotoPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
    override fun getItem(position: Int): PhotoViewFragment {
        // getItem is called to instantiate the fragment for the given page.
        return PhotoViewFragment.newInstance(position, this@PhotoPagerActivity.photos[position])
    }

    override fun getItemId(position: Int): Long {
        return this@PhotoPagerActivity.photos[position].ID!!.hashCode().toLong()
    }

    override fun getItemPosition(`object`: Any): Int {
        val frag = `object` as PhotoViewFragment
        val idx = this@PhotoPagerActivity.photos.indexOfFirst { ph -> ph.ID == frag.dataBinding.photo.ID }
        return if (idx >= 0) idx else PagerAdapter.POSITION_NONE
    }

    override fun getCount(): Int {
        return this@PhotoPagerActivity.photos.size
    }
}

【讨论】:

    【解决方案3】:

    我知道这个问题有点老了,但知道 Google 最近用 ViewPager2 解决了这个问题可能会很有趣。见例子here

    首先,在您的 build.gradle 文件中添加以下依赖项:

      dependencies {
         implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
      }
    

    现在您可以将 xml 文件中的 ViewPager 替换为:

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    

    然后你需要在你的活动中用 ViewPager2 替换 ViewPager

    ViewPager2 需要 RecycleView.Adapter 或 FragmentStateAdapter :

        public class TabAdapter extends FragmentStateAdapter {
            private final List<Tab> tabs;
    
            public TabAdapter(@NonNull FragmentManager fm, List<Tab> tabs, Lifecycle lifecycle) {
                super(fm, lifecycle);
    
                this.tabs = tabs;
            }
    
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                //create your fragment here
            }
    
            @Override
            public int getItemCount() {
                return tabs.size();
            }
    
            @Override
            public long getItemId(int position) {
                // use a distinct id for each item to allow the adapter to see the changes
            }
        }
    

    如果您使用的是 TabLayout,则可以使用 TabLayoutMediator :

            TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
                @Override
                public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                    // configure your tab here
                    tab.setText(tabs.get(position).getTitle());
                }
            });
    
            tabLayoutMediator.attach();
    

    为了删除数据,在从集合中删除项目后,您可以调用 notifyDataSetChanged() 方法或更具体地说是 notifyItemRemoved() 与删除项目的位置

    【讨论】:

    • 请添加如何删除数据的示例,我会批准您的回答。谢谢!
    • 我刚刚编辑了我的答案以显示如何删除该项目。我还在适配器中添加了 getItemId 方法,适配器可能需要它来监视更改
    • 根据getItemId(int) 的JavaDoc,您还应该覆盖containsItem(long) When overriding, also override {@link #containsItem(long)}.
    【解决方案4】:

    您可以尝试使用 FragmentPagerAdapter 而不是 FragmentStatePagerAdapter。问题是,我也遇到过这个问题,它帮助了我。

    【讨论】:

    • 单行解决方案应在评论中发布。
    【解决方案5】:

    您必须先将适配器设置为 null,然后再将其重新设置:

    int currentPosition = mViewPager.getCurrentItem();
    mPagerAdapter.notifyDataSetChanged();
    mViewPager.setAdapter(null);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setCurrentItem(currentPosition);
    

    【讨论】:

    • int getItemPosition(Object object) { return POSITION_NONE; }
    【解决方案6】:

    区分Fragment然后android系统会负责回收Fragment。

    例如 - 覆盖 getItemId 提供唯一 ID。

    FragmentStateAdapter (ViewPager2) - Kotlin 版本。

    class HomeViewPagerAdapter(
    fa: FragmentActivity
    ) : FragmentStateAdapter(fa) {
    
    lateinit var fragments: ArrayList<Fragments>
    
    override fun getItemCount(): Int {
        return fragments.size
    }
    
    override fun getItemId(position: Int): Long {
        return fragments[position].id.ordinal.toLong()
    }
    override fun createFragment(position: Int): Fragment {
        return fragments[position].fragment
    }
    }
    data class Fragments (val id: FragmentId, val fragment: Fragment)
    enum class FragmentId {
        PageOne, 
        PageTwo,
        PageThree
    }
    

    更新适配器

        private fun setupAdapter(fragments: ArrayList<Fragments>) {
        if (adapter != null) {
            adapter?.fragments = fragments
            adapter?.notifyDataSetChanged()
        } else {
            adapter = HomeViewPagerAdapter(requireActivity())
            adapter?.fragments = fragments
            binding.homeViewPager.adapter = adapter
            binding.locationDisabledLl.setOnClickListener {
                findNavController().navigate(HomeFragmentDirections.actionHomeToPermission())
            }
        }
    }
    

    【讨论】:

      【解决方案7】:

      您可以尝试使用 FragmentStatePagerAdapter 而不是 FragmentPagerAdapterpublic

      它对我有用..!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-17
        • 1970-01-01
        • 2017-05-07
        相关资源
        最近更新 更多