ViewPager的无限适配器

11

我一直在使用CWAC的EndlessAdapter实现ListView的无限滚动。

我想要为ViewPager实现相同的功能。不幸的是,PageAdapter和ListAdapter没有共同的基类。

如何最好地实现?是否已经存在可以处理此功能的库?


1
如果您已经实现了,请在gitHub或任何您喜欢的地方分享您的解决方案 :) - AZ_
3个回答

15
什么是最佳方法?
将“无限”逻辑添加到自己的PagerAdapter实现中。或者,如果希望,尝试创建一个装饰PagerAdapter,就像EndlessAdapter装饰常规Adapter一样。
后者可能有些棘手,因为PagerAdapter设计用于页面为视图或片段,并且在类似FragmentPagerAdapter的类内部处理片段会有点可怕。
是否存在已处理此功能的库?
据我所知,没有这样的库。
主要原因是使用情况似乎不那么引人注目。对于ListView,用户可以快速滑动列表,快速滚动数十或数百行。因此,使用“到达结尾”作为触发器来加载更多数据似乎是合理的。然而,对于ViewPager,通常需要更长时间才能到达结尾,特别是如果您没有使用PagerTabStrip或等效物。因此,等待用户一直到达结尾才开始加载其他数据似乎会让用户感到烦恼-您有很多时间去检索更多数据,但没有使用它。
因此,另一种选择是为ViewPager注册一个ViewPager.OnPageChangeListener。当onPageSelected()被调用,并且您认为自己已接近结尾时,启动一个AsyncTask(或其他任务)去收集更多数据。然后的问题是,您需要更新PagerAdapter使用的数据,并在数据更新后调用notifyDataSetChanged() 方法来更新适配器。

当然有用例 - 特别是当片段实际上不依赖于加载大量数据时,但例如ViewPager的位置仅反映您查看的数据的索引。例如:我想要一个ViewPager,每天一页,从今天开始向右延伸到 - 好吧,如果您愿意,就是无限的。翻页只意味着切换要显示的日期,要显示的数据只是计算而不是加载。我仍然不确定如何实现这一点... - Zordid
@Zordid:如我在答案中所述,要创建一个“无限”的ViewPager,你必须将该逻辑放在自定义的PagerAdapter实现中。 FragmentPagerAdapterFragmentStatePagerAdapter都无法做到这一点,因为它们会为每个页面创建新的片段,导致内存不足。 - CommonsWare
目前我使用的是FragmentStatePagerAdapter,到目前为止我还没有达到限制。但我对此不确定,因为状态保存 - 我的视图实际上没有状态,因为每一页都只是反映了日历中选定的一天...我想我应该再次用我的自定义适配器重写它 - 或者只需切换到Views而不是Fragments... - Zordid
不要仅仅摆脱FragmentPagerAdapter没有显示的片段,我认为它每次只使用最多3个。 - android developer
@androiddeveloper:页面限制是ViewPager的事情,而不是PagerAdapter的事情。上次我查看FragmentPagerAdapter源代码时,并没有删除页面,这也是拥有FragmentStatePagerAdapter的原因(它会删除页面但保留每个页面状态的捆绑包)。 - CommonsWare
如果您使用 destroyItem 正确地删除片段(如此:https://i.stack.imgur.com/9zAKR.png),它将被 GC 回收(通过在片段的 finalize 上写入日志进行检查),因此我认为不会有内存泄漏。奇怪的是,默认情况下没有以这种方式实现。我尝试过没有这个实现,我从未看到片段被 GC 回收。我认为它们从未真正被删除,即使似乎代码应该处理它。似乎所有创建的片段都在缓存中。至少它不会重新创建已经访问过的片段。 :) - android developer

6
@Override
public int getCount() {
    return (Integer.MAX_VALUE);
    //artificially large value for infinite scrolling
}

public int getRealCount(){
//Do something to return the actual number of objects.
}   


@Override
public Object instantiateItem(ViewGroup container, int position) {  
    int virtualPosition = position % getRealCount();
    return instantiateVirtualItem(container, virtualPosition);
}

public Object instantiateVirtualItem(ViewGroup container, final int position) {             
//Do the required part here
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    int virtualPosition = position % getRealCount();
    destroyVirtualItem(container, virtualPosition, object);
}


public void destroyVirtualItem(ViewGroup container, int position, Object object){
    container.removeView((View) object);            
}

现在,最重要的部分。
pager.setOffscreenPageLimit(10); //your choice
pager.setCurrentItem(Integer.MAX_VALUE/2,false);
//pager is the ViewPager object

PS:我已经成功实现了这个。如果您还有疑问,请询问。


@stefan 找到了记得在这里通知。 - Shakti
1
猜测唯一的选择是修改ViewPager。对于我的情况,一个可滑动的日历,通过DateTime获取项目而不是整数也会很棒... - stefan
嗨@Shakti,我尝试实现你的方法,但我的应用程序会卡住(内存不足)。我使用RecyclerView尝试了类似的方法,它可以工作,但是ViewPager不行。有什么想法吗? - 4ndro1d

5

你可以通过以下方式“虚假模拟”:

你可能会显示大量页面。使用 FragmentStatePagerAdapter 类: https://developer.android.com/reference/android/support/v13/app/FragmentStatePagerAdapter.html

实现 getCount 方法,返回 Integer.MAX_VALUE
实现 getItemPosition 方法,总是返回 POSITION_NONE
根据需要实现 getItem 方法,返回适当的 Fragment。

然后,在承载 ViewPager 的 Activity 启动时,将 ViewPager 的初始位置设置为非常大的数字,例如:viewPager.setCurrentItem(Integer.MAX_VALUE / 2);

我自己没有尝试过...,结果可能因人而异! :)


使用Integer.MAX_VALUE会出现性能问题并阻塞UI线程。 - Misha Akopov

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