在 FragmentStatePagerAdapter 中动态添加或删除随机位置的页面时出现问题

issues while add or delete of pages at random positions in FragmentStatePagerAdapter dynamically

我花了很多时间研究 stakcoverflow 中的相关 questions/answers 和 google 中关于在 FragmentStatePagerAdapter 中随机位置添加或删除页面的 android 文档。
它工作正常但在一种情况下失败。
可行的场景 1:
添加,比如说,4 页(片段)。
删除位于位置 3 的最后位置的页面。
再次在页尾添加一页。
删除位于位置 3 的最后位置的页面。
……我重复这个步骤,它工作得很好。

但是方案 2 失败了:
添加,比如说,4 页(片段)。
删除除最后位置以外的任何页面,例如位置 2。工作正常。页面不再显示。
再次在页尾添加一页。此处失败..我看到空白页。

我确实在一定程度上进行了分析,发现getItem() 失败时并没有被调用。由于 getItem() 是创建和 returns 新片段的方法,因此它不会获取新片段,因此它显示为空白。我看到了 instantiateItem() 的实现,并观察到如果可用片段数的大小大于为其创建片段所请求的位置,则不会调用 getItem() 。但不确定它何时或如何发生。

这是我的适配器中的 getItem() 和 getItemPosition()。我有自己的一组 ArrayList 片段(FragmentStatePagerAdapter 维护它自己的一组片段列表)...mFragments 是在下面的代码中。在我调用 notifyDataItemChanged() 之后,我观察到了。

@Override
public int getItemPosition(Object object) {
    if (object instanceof InnerFragment) {
        if (!mFragments.contains(object)) {
            Log.d(TAG, "getItemPosition Frag " + object + " is REMOVED and item count is " + MainFragment.mPager.getChildCount());
            return POSITION_NONE;
        } else {
            Log.d(TAG, "getItemPosition Frag " + object + " is AVAILABLE and item count is " + MainFragment.mPager.getChildCount());
            Log.d(TAG, "getItemPosition .....position of this fragment is : " + mFragments.indexOf(object));
            return mFragments.indexOf(object);
        }
    }
    Log.d(TAG, "getItemPosition object is not InnerFragment..");
    return super.getItemPosition(object);
}

@Override
public Fragment getItem(int position) {
    Log.d(TAG, "getItem for position " + position + "..create NEW FRAG and item count is " + MainFragment.mPager.getChildCount());
    return createNewPage(position);
}

如果您有任何线索,请抽出几分钟帮助我。 下面是日志片段。第一个是当我删除了第 3 个位置的页面(这是页面的中间,而不是最后一页)。第二个是当我尝试添加新页面时。当我在第二个日志片段中添加页面时,您可以观察到没有调用 getItem() ..

08-20 12:09:55.062    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   Places/count before handlePlaces : 6 and fragments : [InnerFragment{3e50591d #0 id=0x7f10008d}, InnerFragment{ccaf792 #1 id=0x7f10008d}, InnerFragment{16c61563 #2 id=0x7f10008d}, InnerFragment{3a6d7560 #3 id=0x7f10008d}, InnerFragment{229e6a19 #4 id=0x7f10008d}, InnerFragment{2a1cfade #5 id=0x7f10008d}]
08-20 12:09:55.062    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   Place deleted : 110665902
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   Places/count after handlePlaces : 5 and fragments : [InnerFragment{3e50591d #0 id=0x7f10008d}, InnerFragment{ccaf792 #1 id=0x7f10008d}, InnerFragment{16c61563 #2 id=0x7f10008d}, InnerFragment{229e6a19 #4 id=0x7f10008d}, InnerFragment{2a1cfade #5 id=0x7f10008d}]
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{3e50591d #0 id=0x7f10008d} is AVAILABLE and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 0
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{ccaf792 #1 id=0x7f10008d} is AVAILABLE and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 1
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{16c61563 #2 id=0x7f10008d} is AVAILABLE and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 2

08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{3a6d7560 #3 id=0x7f10008d} is REMOVED and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   destroyItem object InnerFragment{3a6d7560 #3 id=0x7f10008d}to be rmeoved from 3

08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{229e6a19 #4 id=0x7f10008d} is AVAILABLE and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 3
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{2a1cfade #5 id=0x7f10008d} is AVAILABLE and item count is 6
08-20 12:09:55.072    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 4




08-20 12:10:10.152    3919-3919/ com.example.pack D/MainActivity﹕   all received for Place{placeId=119680744, placeName='Moskva', localName='null', latitude=55.75222, longitude=37.61556, moh=150, countryId=1426, categoryId=1552, categoryName='Hovedstad', country='Russia', priority=0, adm1='null', adm2='Moscow'}
08-20 12:10:15.152    3919-3919/ com.example.pack D/MainActivity﹕   all received for Place{placeId=113259163, placeName='Stockholm', localName='null', latitude=59.33258, longitude=18.0649, moh=21, countryId=428, categoryId=1552, categoryName='Hovedstad', country='Sweden', priority=0, adm1='Stockholm', adm2='Stockholm municipality'}
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   Places/count before handlePlaces : 5 and fragments : [InnerFragment{3e50591d #0 id=0x7f10008d}, InnerFragment{ccaf792 #1 id=0x7f10008d}, InnerFragment{16c61563 #2 id=0x7f10008d}, InnerFragment{229e6a19 #4 id=0x7f10008d}, InnerFragment{2a1cfade #5 id=0x7f10008d}]
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   New Place added : 105109562
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   Places/count after handlePlaces : 6 and fragments : [InnerFragment{3e50591d #0 id=0x7f10008d}, InnerFragment{ccaf792 #1 id=0x7f10008d}, InnerFragment{16c61563 #2 id=0x7f10008d}, InnerFragment{229e6a19 #4 id=0x7f10008d}, InnerFragment{2a1cfade #5 id=0x7f10008d}]
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{3e50591d #0 id=0x7f10008d} is AVAILABLE and item count is 5
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 0
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{ccaf792 #1 id=0x7f10008d} is AVAILABLE and item count is 5
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 1
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{16c61563 #2 id=0x7f10008d} is AVAILABLE and item count is 5
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 2
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{229e6a19 #4 id=0x7f10008d} is AVAILABLE and item count is 5
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 3
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition Frag InnerFragment{2a1cfade #5 id=0x7f10008d} is AVAILABLE and item count is 5
08-20 12:10:25.112    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   getItemPosition .....position of this fragment is : 4

08-20 12:10:25.122    3919-3919/ com.example.pack D/MyFragStatePagerAdapter﹕   instantiateItem for position 5 and item count is 5

没想到是框架问题。但是在我花了很多时间分析它之后,我发现 FragmentStatePagerAdapter 中的 destroyItem() 没有删除 getItemPosition() 返回 POSITION_NONE 的片段。它分配 null 但它不会删除该项目。下面是来自原始 FragmentStatePagerAdapter 的方法。你看我已经添加了两行代码来明确删除片段。

    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment)object;
        if(this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        while(this.mSavedState.size() <= position) {
            this.mSavedState.add(null);
        }

        this.mSavedState.set(position, this.mFragmentManager.saveFragmentInstanceState(fragment));
        this.mFragments.set(position, null);

        /***** Below are the two lines I have added  
        in addition to this original method from Android. ******/
        this.mFragments.remove(position);
        this.mSavedState.remove(position);

        this.mCurTransaction.remove(fragment);
    }

正如我在日志中清楚看到的那样,删除页面后的页面索引没有正确重新排序。

InnerFragment{3e50591d #0 id=0x7f10008d}
InnerFragment{ccaf792 #1 id=0x7f10008d}
InnerFragment{16c61563 #2 id=0x7f10008d}
InnerFragment{229e6a19 #4 id=0x7f10008d}
InnerFragment{2a1cfade #5 id=0x7f10008d}

我删除了第三个位置。以上是容器中的片段列表,您可以在其中看到索引 #3 丢失。那是因为有一个元素是空的..它没有被完全删除,但如果你观察的话,它只是被原始代码引用的空。
我的情况是我应该能够删除任何页面并且应该能够在末尾添加一个新页面。
因此,我明确删除了带有上述 destroyItem() 方法中行的对象。它奏效了。

最后但同样重要的是,我在得到答案后发现了这个 link。看到这个 https://code.google.com/p/android/issues/detail?id=37990。许多开发人员似乎遇到了多个问题,但针对他们的特定场景,他们也有解决方案。看看你的场景是否涵盖在那里。如果没有你可以做我做的..解除完整的FragmentStatePagerAdapter.java并根据需要进行修改。