Removing fragments from FragmentStatePagerAdapter

前端 未结 3 978
轻奢々
轻奢々 2020-12-05 20:59

UPDATE

After some major fighting with this problem and help from SO users I managed to solve it. This is how my clearProgressUpTo looks

相关标签:
3条回答
  • 2020-12-05 21:08

    FragmentStatePageAdapter saves the state of the fragment while destroying it, so that latter when the fragment is recreated the saved state will be applied to it.

    The fragment state is stored in a private member variable. Hence we can't extend the current implementation to add the feature of removing the saved state.

    I've cloned the current FragmentStatePagerAdapter implementation and have included the saved state removal functionality. Here is the gist reference.

       /**
         * Clear the saved state for the given fragment position.
         */
        public void removeSavedState(int position) {
            if(position < mSavedState.size()) {
                mSavedState.set(position, null);
            }
        }
    

    In your clearProgressUpTo method, whenever you remove the fragment from sparse array, call this method, which clears off the saved state for that fragment.

    0 讨论(0)
  • 2020-12-05 21:10

    Your problem is not caused by your use of SparseArray. In fact, you meet a bug of the FragmentStatePagerAdapter. I spent some time reading the source code of FragmentStatePagerAdapter, ViewPager and FragmentManagerImpl and then found the reason why your new fragments have the state of old ones:

    The FragmentStatePagerAdapter save the sate of your old fragments when it remove them, and if you add new fragments to you adapter later, the saved state will be passed to your new fragment at the same location.

    I googled this problem and find a solution, here is its link: http://speakman.net.nz/blog/2014/02/20/a-bug-in-and-a-fix-for-the-way-fragmentstatepageradapter-handles-fragment-restoration/

    The source code can be found here: https://github.com/adamsp/FragmentStatePagerIssueExample/blob/master/app/src/main/java/com/example/fragmentstatepagerissueexample/app/FixedFragmentStatePagerAdapter.java

    The key idea of this solution is to rewrite the FragmentStatePagerAdapter and use a String tag to identify each fragment in the adapter. Because each fragment has a different tag, it is easy to identify a new fragment, then don't pass a saved state to a new Fragment.

    Finally, say thanks to Adam Speakman who is the author of that solution.

    0 讨论(0)
  • 2020-12-05 21:26

    When you call notifyDataSetChanged() the adapter starts removing its fragments using destroyItem(ViewGroup container, int position, Object object).

    However, each fragment's instance state is retained internally by FragmentStatePagerAdapter to be later used in instantiateItem(ViewGroup container, int position).

    Simply save item's position you want to remove and call notifyDataSetChanged(). Just after the last item is destroyed, retrieve the desired saved instance state from FragmentStatePagerAdapter and replace with null.

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
    
        if(position == getCount() - 1 && i >= 0){
            Bundle state = (Bundle) saveState();
            Fragment.SavedState[] states = (Fragment.SavedState[]) state.getParcelableArray("states");
            if (states != null)
                states[i] = null;
            i = -1;
            restoreState(state, ClassLoader.getSystemClassLoader());
        }
    

    Note: getItemPosition(Object object) must return PagerAdapter.POSITION_NONE.

    0 讨论(0)
提交回复
热议问题