使用 getItemPosition(Object object) 对 FragmentStatePagerAdapter 中的页面重新排序 [英] reorder pages in FragmentStatePagerAdapter using getItemPosition(Object object)

查看:45
本文介绍了使用 getItemPosition(Object object) 对 FragmentStatePagerAdapter 中的页面重新排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为 FragmentStatePagerAdapter 在重写 getItemPosition(Object object) 以重新排序页面时行为不正确.

I believe that FragmentStatePagerAdapter does not behave correctly when overriding getItemPosition(Object object) with the purpose of reordering the pages.

下面是一个简单的例子.在初始状态下,页面的顺序是{A, B, C}.调用 toggleState() 后,页面顺序变为 {A, C, B}.通过覆盖 getItemPosition(Object object),我们确保当前正在查看的页面(A、B 或 C)不会改变.

Below is a simple example. In the initial state, the order of the pages is {A, B, C}. Upon calling toggleState(), the order of the pages changes to {A, C, B}. By overriding getItemPosition(Object object), we ensure that the current page being viewed (A, B, or C) does not change.

public static class TestPagerAdapter extends FragmentStatePagerAdapter {
    private boolean mState = true;

    public TestPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

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

    private void toggleState() {
        mState = !mState;
        notifyDataSetChanged();
    }

    private String getLabel(int position) {
        switch (position) {
            case 0:
                return "A";
            case 1:
                return mState ? "B" : "C";
            default:
                return mState ? "C" : "B";
        }
    }

    @Override
    public int getItemPosition(Object object) {
        String label = ((TestFragment) object).getLabel();
        if (label.equals("A")) {
            return 0;
        } else if (label.equals("B")) {
            return mState ? 1 : 2;
        } else {
            return mState ? 2 : 1;
        }
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return getLabel(position);
    }

    @Override
    public Fragment getItem(int position) {
        return TestFragment.newInstance(getLabel(position));
    }
}

我遇到了两种看似不正确的独立行为.

I have encountered two separate behaviours which seem incorrect.

  1. 如果我立即调用 toggleState()(在查看页面 A 时,在滑动到任何其他页面之前),应用程序将崩溃.

  1. If I immediately call toggleState() (while viewing page A, before swiping to any other page), the app crashes.

java.lang.IndexOutOfBoundsException: Invalid index 2, size is 2
  at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
  at java.util.ArrayList.set(ArrayList.java:477)
  at android.support.v4.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:136)
  at android.support.v4.view.ViewPager.populate(ViewPager.java:867)
  at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:469)
  at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:441)
  at android.support.v4.view.ViewPager.dataSetChanged(ViewPager.java:766)
  at android.support.v4.view.ViewPager$PagerObserver.onChanged(ViewPager.java:2519)
  at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
  at android.support.v4.view.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:276)
  at com.ugglynoodle.test.testfragmentstatepageradapter.MainActivity$TestPagerAdapter.toggleState(MainActivity.java:55)
  ...

查看 FragmentStatePagerAdapter 的源代码,可以通过在调用 set() 在第 136 行.

Looking at the source of FragmentStatePagerAdapter, this would be fixed by first checking the size of mFragments (as in lines 113-115) before calling set() in line 136.

如果我先滑动到页面 B,然后调用 getItem(2),创建页面 C,mFragments 现在的大小为 3 (这将防止上述崩溃立即发生).然后我滑回页面 A,页面 C 被破坏,因为它应该是(因为它是 2 页,并且我使用默认的屏幕外页面限制 1).现在,我调用 toggleState().页面 B 现已销毁.但是,不会重新创建页面 C!这意味着,当我现在向右滑动时,我会看到一个空白页面.

If I first swipe to page B, then getItem(2) is called, page C is created, and mFragments now has a size of 3 (this will prevent the crash above from happening in a moment). Then I swipe back to page A, and page C is destroyed, as it should be (since it is 2 pages away, and I'm using the default offscreen page limit of 1). Now, I call toggleState(). Page B is now destroyed. However, page C is NOT recreated! This means, when I now swipe to the right, I get an empty page.

首先,很高兴知道我是否正确并且这些实际上是错误,或者我是否做错了什么.如果它们是错误,任何人都可以提出解决方法(除了自己调试和重建支持库)吗?肯定有人成功地覆盖了 getItemPosition(Object object)(除了将所有内容都设置为 POSITION_NONE)?

First, it would be nice to know whether I'm correct and these are in fact bugs, or whether I'm doing something wrong. If they are bugs, can anyone suggest a workaround (other than debugging and rebuilding the support library myself)? Surely somebody must have overridden getItemPosition(Object object) successfully (apart from setting everything to POSITION_NONE)?

我正在使用支持库的当前版本 (10).

I am using the current revision (10) of the support library.

推荐答案

看了下FragmentStatePagerAdapter的源码,搞清楚到底是哪里出了问题.FragmentStatePagerAdapter 在 ArrayLists 中缓存片段和保存状态:mFragmentsmSavedState.但是当片段被重新排序时,没有重新排序 mFragmentsmSavedState 元素的机制.因此,适配器会向寻呼机提供错误的片段.

Looking at the source of FragmentStatePagerAdapter, I figured out exactly what is going wrong. The FragmentStatePagerAdapter caches the fragments and saved states in ArrayLists: mFragments and mSavedState. But when the fragments are reordered, there's no mechanism for reordering the elements of mFragments and mSavedState. Therefore, the adapter will provide the wrong fragments to the pager.

我为此提交了一个问题,并附上了一个固定的实现(NewFragmentStatePagerAdapter.java) 的问题.在修复中,我向 FragmentStatePagerAdapter 添加了一个 getItemId() 函数.(这反映了 FragmentPagerAdapter 中的重新排序实现.)始终按适配器位置存储 itemId 的数组.然后,在 notifyDataSetChanged() 中,适配器检查 itemIds 数组是否已更改.如果有,则 mFragmentsmSavedState 会相应地重新排序.进一步的修改可以在 destroyItem()saveState()restoreState() 中找到.

I've filed an issue for this, and attached a fixed implementation (NewFragmentStatePagerAdapter.java) to the issue. In the fix, I've added a getItemId() function to FragmentStatePagerAdapter. (This mirrors the reordering implementation in FragmentPagerAdapter.) An array of the itemIds by adapter position is stored at all times. Then, in notifyDataSetChanged(), the adapter checks if the itemIds array has changed. If it has, then mFragments and mSavedState are reordered accordingly. Further modifications can be found in destroyItem(), saveState() and restoreState().

要使用这个类,getItemPosition()getItemId() 必须与 getItem() 一致地实现.

To use this class, getItemPosition() and getItemId() must be implemented consistently with getItem().

这篇关于使用 getItemPosition(Object object) 对 FragmentStatePagerAdapter 中的页面重新排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆