ViewPager第一个fragment始终显示错误,使用FragmentStatePagerAdapter。

9

我尝试使用与PlayStore 5.1.x相同的视图翻页器+选项卡设计。这是我的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical|center_horizontal"
    android:gravity="center_vertical|center_horizontal"
    android:orientation="vertical">

    <com.astuetz.PagerSlidingTabStrip
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/background_tabs" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

我的适配器:

public class MainPagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<FakeFragment> fragments;

    public MainPagerAdapter(FragmentManager fm) {
        super(fm);
        // TODO Auto-generated constructor stub
        fragments = new ArrayList<FakeFragment>();
    }

    @Override
    public Fragment getItem(int position) {
        // TODO Auto-generated method stub      
        if(position < getCount()) {
            FakeFragment fragment = FakeFragment.newInstance(position);
            fragments.add(fragment);
        }
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return Category.values().length;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        // TODO Auto-generated method stub
        return Category.values()[position].getTitle();
    }

    @Override
    public int getItemPosition(Object object) {
        // TODO Auto-generated method stub
        return POSITION_NONE;
    }
}

我的选项卡和页面导航显示正确!但是我注意到ViewPager中显示的第一个片段总是与第二个相同。然后当我向左或向右滑动一次或两次并返回第一页时,我发现现在显示了正确的片段!!我不明白为什么会这样,请给我一些解释。
解决方法:问题出在我的FakeFragment.newInstance()方法定义上。
private static int position;

public static FakeFragment newInstance(int position) {
    // TODO Auto-generated method stub
    FakeFragment.position = position;
    return new FakeFragment();
}

我通过在我的FakeFragment实例上使用setArguments(args),然后在onCreate方法中检索它来更改了它。现在一切都很顺利!
有人能解释一下为什么吗?
我认为,这样做的话,位置的值将完全取决于片段的生命周期,因此位置将始终是预期的位置,对吗?

1
摆脱 private ArrayList<FakeFragment> fragments,只需让 getItem() 返回片段的新实例即可。FragmentStatePagerAdapter完整和整个目标 就是不要将所有片段都保留在内存中。如果这是您想要的,请仍然摆脱 ArrayList<FakeFragment> 并将适配器切换为 FragmentPagerAdapter 而不是 FragmentStatePagerAdapter。同时也可以摆脱 getItemPosition()顺便说一句,在这里有一系列使用 ViewPager 的示例应用程序 - CommonsWare
1
你的实现应该在两种情况下都能正确工作,唯一不同的可能是速度。如果加载数据很昂贵,并且您不需要每次都这样做,请使用FragmentStatePagerAdapter,使用Fragment.onSaveInstanceState来存储已加载的数据集。 - Eugen Pechanec
谢谢@EugenPechanec,我会尝试你建议的做法。 - S.Thiongane
即使今天我使用非常相似的代码仍然遇到同样的问题。一个数组有 [a, b, c, d]..应该是页面的顺序。但是我的页面第一次显示为[b, b, c, d]。向后滑动时,c、b、a或d、c、b、a。日志显示第一个适配器获取了值a和位置0,但'a'从未显示出来。仍在想到底哪里出了问题。 - lini sax
请查看我的分析以及我找出问题的方法stackoverflow.com/a/30968767/816635。 - lini sax
显示剩余3条评论
2个回答

6

1) 如果你不处理它,请不要实现getItemPosition(Object)。你不需要实现它,而且如果实现不正确可能会破坏其他功能。

2) getItem(int) 的目的是返回一个新的片段。舍弃片段数组是没有意义的。

3) 让适配器类成为static(这样有助于可重用性,适配器不应依赖父类来获取其数据集,对吧?)并将Category作为构造函数参数传递。将其存储在变量中,并根据此数据集生成新片段。您还可能希望将Category[position]作为片段构造函数的参数,而不仅仅是position


谢谢@Eugen,请问您能否提供更多关于第三点的信息:“适配器不应该依赖于父类来获取其数据集”? - S.Thiongane
1
最佳实践是将嵌套类声明为“静态”。这会删除它们与父类的隐式连接(请注意,您无法访问父类的字段变量)。在某些情况下,这可能会防止内存泄漏。只有在知道自己在做什么时才使用非静态内部类。尝试阅读此内容:[https://dev59.com/GXVD5IYBdhLWcg3wJIIK#70358]。 - Eugen Pechanec
我已经接受了你的答案并给予了奖励,因为你的回答和评论带领我找到了其他有用的信息。 - S.Thiongane
我很高兴能够帮助你。祝你的应用程序好运。 - Eugen Pechanec
请查看以下帖子:http://stackoverflow.com/questions/33012056/view-pager-first-and-second-fragment-data-is-not-updating-when-data-changed-in-a - kartheeki j

3
getItem() 实现是问题所在。
@Override
public Fragment getItem(final int position) {
    return FakeFragment.newInstance(position);
}

在这个get方法中,您绝不应更改数据:不要在其中调用add()。我怀疑此时Adapter不会知道您添加了一个元素。


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