使用新顺序更新ViewPager中的Fragment

7
我有一个ViewPager,它从服务器传下来的数据绘制其页面 (视图)。偶尔,服务器会发送新数据,这将重新排序视图(有时还会添加新视图),我需要在 Android 设备上实现无缝切换,而用户正在查看特定片段。
当从服务器下载新数据时,我已经设置它调用 notifyDataSetChanged() 方法,但似乎它仍将当前查看的幻灯片左右两侧的缓存版本保留下来,而这些版本可能会随着重新排序而更改。我已经查看了这个问题:ViewPager PagerAdapter not updating the View 并实施了第一个解决方案,除了重新加载当前正在查看的幻灯片外,其他都很好用,这对我的目的不起作用。我浅显地研究了在同一页的第二个答案中提出的 setTag() 方法的实现方式,这可以用于更新幻灯片中的信息,但不能帮助我重新排序它们。
有没有一种方法可以在幕后重新排列所有幻灯片而不会引起当前查看的幻灯片中断?
TIA
编辑:添加代码并要求进一步澄清
这是我的 Adapter 类。
    private class ScreenSlidePagerAdapter extends FragmentPagerAdapter {
    ContestEntriesModel entries;

    public ScreenSlidePagerAdapter(FragmentManager fm, ContestEntriesModel Entries) {
        super(fm);
        this.entries = Entries;
    }

    @Override
    public long getItemId(int position) {
        return entries.entries[position].ContestEntryId;

    }

    @Override
    public int getItemPosition(Object object) {
        android.support.v4.app.Fragment f = (android.support.v4.app.Fragment)object;
        for(int i = 0; i < getCount(); i++){

            android.support.v4.app.Fragment fragment = getItem(i);
            if(f.equals(fragment)){
                return i;
            }
        }
        return POSITION_NONE;
    }


    @Override
    public android.support.v4.app.Fragment getItem(int position) {

        long entryId = getItemId(position);
        //Log.d("EntryIds",Integer.toString(entryId));
        if(mItems.get(entryId) != null) {
            return mItems.get(entryId);
        }
        Fragment f = ScreenSlidePageFragment.create(position);
        mItems.put(entryId, f);
        return f;
    }

    @Override
    public int getCount() {
        return NUM_PAGES;
    }
}

当新的有效负载从服务器传来时,我使用以下内容:

    @Override
    protected void onPostExecute(ContestEntriesModel entries) {
        Fragment currentFragment = ContestEntries.mItems.get(ContestEntries.CurrentEntryId);
        Log.d("fromUpdate",Long.toString(ContestEntries.CurrentEntryId));
        ContestEntries.Entries = entries;
        ContestEntries.NUM_PAGES = ContestEntries.Entries.entries.length;
        ContestEntries.mPagerAdapter.notifyDataSetChanged();
        ContestEntries.mPager.setCurrentItem(ContestEntries.mPagerAdapter.getItemPosition(currentFragment));
    }

在你的实现中调用 getItem(int) 会每次创建一个新的 Fragment,因为你没有实现某种形式的缓存。由于你每次都创建一个新的 Fragment,getItemPosition(Object) 将始终返回 POSITION_NONE,这就是为什么它会重新加载当前的幻灯片。 - Reinier
好的,你能给我指一些资源,让我学习如何实现某种形式的缓存吗?另外,我正在使用的片段对象没有newInstance()方法... - Christopher Johnson
解释:从我的示例中去掉缓存后,作为“Object”传递的Fragment将永远不会等于getItem(int)的返回值,因为对象引用不同。虽然您可以通过在Fragment中实现一个“equals()”方法来克服这个问题,但这仍意味着每次调用getItem(int)都会创建一个新的Fragment - 这是对资源的无用浪费。 - Reinier
好的,我很接近了。现在当服务器有新的更新时,它可以保持在同一张幻灯片上。然而,它似乎会将幻灯片缓存到其旁边。我该如何强制刷新所有内容?目前,我只是按升序发送7张幻灯片,然后在从服务器收到更新时,按降序再次发送它们(出于测试目的)。正如我所说,我能够在从服务器进行新加载时保持当前幻灯片,但如果我是第一次加载的第二张幻灯片,然后从服务器进行新加载,那么排序不会像我的测试应该那样被反转。 - Christopher Johnson
实际上,顺序是相反的,但我必须向下滑动几张幻灯片才能加载它们。我需要能够刷新除了我当前正在查看的幻灯片之外的所有幻灯片,但我需要将该幻灯片放置在正确的新顺序中。我已更新我的 OP,使用新代码。 - Christopher Johnson
显示剩余3条评论
1个回答

6
请查看我的回答。关键是在您的适配器中同时覆盖getItemId(int)getItemPosition(Object)方法。 getItemId(int)用于在数据集中唯一标识您的页面。getItemPosition(Object)用于获取数据集中此唯一页面的(更新后的)位置。
如果您希望在更新(添加/删除页面)前后保持对同一页面的关注,则应在调用适配器上的.notifyDataSetChanged()之后,为页面的新位置调用viewpager.setCurrentItem(int)

这看起来非常有前途。诚然,我不是安卓开发者,这是我的第一个真正的项目之一。我会尝试您的建议,但如果您不介意,我准备问一些关于实现的问题。 - Christopher Johnson
好的,我遇到了第一个难题。 我的方法是从服务器传递“幻灯片”的总数。然后,我用与我的调制解调器没有直接联系的数据填充一个数组(没有真正的“数据集”),在Fragment扩展类中,我根据通过mPageNumber传递的数组索引来填充幻灯片。因此,我认为我没有真正的“键”,可以在您链接的页面上使用getItemId()示例。如果您认为这有帮助,我可以在我的OP中发布一些代码。 - Christopher Johnson
你从服务器接收到什么样的项目?它们难道不包含某种形式的唯一标识符吗?否则,您可以通过对单个项目进行哈希处理来构造标识符。由于getItemId(int)ViewPager用于识别唯一页面条目的方式,因此在涉及具有动态位置的页面时,这是正确的方法。是的 - 示例代码可能会有所帮助。 - Reinier
我意识到可以使用来自服务器的唯一ID。但我仍然面临一些挑战。我会更新我的原始问题帖子,并解释我现在面临的新挑战。 - Christopher Johnson

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