FragmentPagerAdapter的getItem方法未被调用

129

我无法在FragmentPagerAdapter中重复使用Fragment。虽然使用destroyItem()方法删除了该片段,但仍不会再次调用getItem()方法。由于只有2-3个图像,因此我使用FragmentPagerAdapter而不是FragmentStatePagerAdapter。

public class ExamplePagerAdapter extends FragmentPagerAdapter {

    ArrayList < String > urls;
    int size = 0;
    public ExamplePagerAdapter(FragmentManager fm, ArrayList < String > res) {
        super(fm);
        urls = res;
        size = urls.size();
    }

    @Override
    public int getCount() {
        if (urls == null) {
            return 0;
        } else {
            return size;
        }

    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();
    }

    @Override
    public Fragment getItem(int position) {

        Fragment fragment = new FloorPlanFragment();
        Bundle b = new Bundle();
        b.putInt("p", position);
        b.putString("image", urls.get(position));
        Log.i("image", "" + urls.get(position));
        fragment.setArguments(b);
        return fragment;
    }
}

在 FragmentActivity 中,

pager.setAdapter(new ExamplePagerAdapter(getSupportFragmentManager(), res2)); 

3
你覆盖destroyItem()的原因是什么?这不是必要的。 - CommonsWare
重新初始化请使用FragmentStatePagerAdapter同时在重写时调用 super.destroyItem(container, position, object); - faiziii
7个回答

316

KISS 答案:

简单使用 FragmentStatePagerAdapter 替代 FragmentPagerAdapter

我得到了答案。一开始我认为应该删除这个问题,因为我犯了一个很愚蠢的错误,但这个答案将帮助那些遇到同样问题的人。使用 FragmentStatePagerAdapter 替代 FragmentPagerAdapter

如 @BlackHatSamurai 在评论中提到的:

这样做的原因是 FragmentStatePagerAdapter 会销毁未被使用的片段,而 FragmentPagerAdapter 不会。


2
讨厌成为又一个“我也是!”的帖子,但是是的 >.< 谢谢不删问题。 - Paul Ruiz
35
这个方法有效的原因是FragmentStatePagerAdapter会销毁未使用的Fragment,而FragmentPagerAdapter则不会。 - BlackHatSamurai
@Blaine 谢谢,你让我豁然开朗。顺便说一句,谢谢兄弟,你救了我于水深火热之中。 - alicanbatur
3
供未来可能在搜索特定问题时遇到此文的人参考:请了解FragmentPagerAdapter和FragmentStatePagerAdapter两者之间的区别,它们有不同的行为方式,你的具体使用情况可能需要选择其中之一。 - Chris Stewart
2
@najibputhawala 我使用了FragmentStatePagerAdapter,但仍然遇到相同的问题。getItem()没有被调用。 - sam_k
显示剩余6条评论

173

使用 FragmentStatePagerAdapter 并不能完全解决我的问题,这个问题与在视图页中的子片段中未调用 onCreateView 的类似问题相似。我实际上是在另一个 Fragment 中嵌套了我的 FragmentPagerAdapter ,因此 FragmentManager 在它们所有之间共享,从而保留旧片段的实例。解决方法是在我的主机片段的构造函数中向 FragmentPagerAdapter 提供 getChildFragmentManager 的实例,例如:

FragmentPagerAdapter adapter = new FragmentPagerAdapter(getChildFragmentManager());

getChildFragmentManager() 方法可以在 Fragment 中访问,这对我很有用,因为它会返回一个私有的 FragmentManager,专门用于需要嵌套 Fragment 的情况。

  • 不过请记住,要使用getChildFragmentManager(),您的最小 API 版本必须至少为 17(4.2),否则可能会遇到问题。当然,如果您正在使用支持库 v4 中的 Fragment,则应该没有问题。

25
这是正确的答案。问题在于嵌套在其他片段中的片段应该使用getChildrenFragmentManager()而不是getFragmentManager()。 - shihpeng
在嵌套片段的同一情况下,我仍然有问题。在我的情况下,我的最后几项(例如9,10)getItem没有被调用!这非常奇怪。 - Mahdi
无法正常工作,因为这个原因: adapter = new MyAdapter(ChildFragmentManager); 会导致以下错误: Error CS1503 Argument 1: cannot convert from 'Android.App.FragmentManager' to 'Android.Support.V4.App.FragmentManager' - Altivo

9

覆盖 long getItemId (int position)

FragmentPagerAdapter 使用 getItem 缓存创建的片段。我也遇到了同样的问题——即使调用了 notifyDataSetChanged()getItem 也没有被调用。

这实际上是一种特性,而不是错误。您需要覆盖 getItemId,以便正确重用您的片段。由于您正在删除片段,因此位置正在改变。如文档中所述:

long getItemId (int position)

为给定位置的项目返回唯一标识符。

默认实现返回给定位置。如果项目的位置可能更改,则子类应覆盖此方法。

只需为每个片段提供唯一的 ID 即可完成。

使用 FragementStatePagerAdapter 或在 int getItemPosition (Object object) 中返回 POSITION_NONE 是错误的。您将无法获得任何缓存。


这个是正确答案。我需要为getItemId中的片段提供不同的ID并调整getItemPosition,以便在应该删除片段时返回POSITION_NONE。我有一个枚举标志来实现这一点。之后将调用getItem - Murat Karagöz
谢谢 @MuratKaragöz 给的 +50 :) - Vedant Agarwala
非常感谢,使用此方法覆盖后效果更佳。 - mcourpot
谢谢,这对我也起作用了。在尝试了所有方法后,这是唯一有效的解决方案。 - Burak Senel

5

我尝试了@kanika和@Jraco11发布的方法,但问题仍然存在。

所以,经过多次尝试,我发现了一个适合我的方法,并将以下代码添加到了我的FragmentPagerAdapter中:

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

根据我所读的,getItemPosition 用于通知 ViewPager 是否需要刷新一个项目,并且如果可见位置的项目没有改变,则避免更新。


4

getItem() 方法仅用于创建新项。一旦它们被创建,该方法将不会被调用。如果您需要获取适配器当前正在使用的项目,请使用此方法:

pagerAdapter.instantiateItem(viewPager, TAB_POS)

2

有两种不同的情况: 1.) 每个页面都具有相同的布局: 在这种情况下,最好是通过继承PagerAdapter来扩展您的自定义适配器,并返回单个布局。

2.) 每个页面都具有不同的布局: 在这种情况下,最好是通过继承FragmentStatePagerAdapter来扩展您的自定义适配器,并为每个页面返回不同的片段。


0
我发现在选项卡布局上设置监听器可以阻止这个被调用,可能是因为它们只有一个监听器的空间 tabLayout.setOnTabSelectedListener 而不是监听器数组。

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