9

I have a ViewPager (extends FragmentPagerAdapter) which holds two Fragments. What I need is just refresh a ListView for each Fragment when I swipe among them. For this I have implemented ViewPager.OnPageChangeListener interface (namely onPageScrollStateChanged). In order to hold references to Fragments I use a HashTable. I store references to Fragments in HashTable in getItem() method:

  @Override
        public Fragment getItem(int num) {
            if (num == 0) {
                Fragment itemsListFragment = new ItemsListFragment();
                mPageReferenceMap.put(num, itemsListFragment);
                return itemsListFragment;
            } else {
                Fragment favsListFragment = new ItemsFavsListFragment();
                mPageReferenceMap.put(num, favsListFragment);
                return favsListFragment;
            }
        }

So when I swipe from one Fragment to another the onPageScrollStateChanged triggers where I use the HashTable to call required method in both Fragments (refresh):

 public void refreshList() {
        ((ItemsListFragment) mPageReferenceMap.get(0)).refresh();
        ((ItemsFavsListFragment) mPageReferenceMap.get(1)).refresh();
    }

Everything goes fine until orientation change event happens. After it the code in refresh() method, which is:

public void refresh() {
        mAdapter.changeCursor(mDbHelper.getAll());
        getListView().setItemChecked(-1, true); // The last row from a exception trace finishes here (my class).
    }

results in IllegalStateException:

java.lang.IllegalStateException: Content view not yet created
        at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
        at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
        at ebeletskiy.gmail.com.passwords.ui.ItemsFavsListFragment.refresh(ItemsFavsListFragment.java:17)

Assuming the Content view is not created indeed I set the boolean variable in onActivityCreated() method to true and used if/else condition to call getListView() or not, which shown the activity and content view successfully created.

Then I was debugging to see when FragmentPagerAdapter invokes getItem() and it happens the method is not called after orientation change event. So looks like it ViewPager holds references to old Fragments. This is just my assumption.

So, is there any way to enforce the ViewPager to call getItem() again, so I can use proper references to current Fragments? May be some other solution? Thank you very much.

Eugene
  • 59,186
  • 91
  • 226
  • 333

3 Answers3

5

Then I was debugging to see when FragmentPagerAdapter invokes getItem() and it happens the method is not called after orientation change event. So looks like it ViewPager holds references to old Fragments.

The fragments should be automatically recreated, just like any fragment is on an configuration change. The exception would be if you used setRetainInstance(true), in which case they should be the same fragment objects as before.

So, is there any way to enforce the ViewPager to call getItem() again, so I can use proper references to current Fragments?

What is wrong with the fragments that are there?

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I don't use `setRetainInstance()` in there. The problem is in the exception which pops up after `orientation change`. I just assume the old Fragments are in use, because I checked the condition the `onActivityCreated()` is executed for both of them. – Eugene Sep 03 '12 at 22:15
  • @siik: When are you calling this `refresh()` method? And when are you pulling out the newly-created fragments to populate your (ick) `Hashtable`? – CommonsWare Sep 03 '12 at 22:17
  • I am calling this `refresh()` method in `onPageScrollStateChanged()` (which is implementation of `ViewPager.OnPageChangeListener` interface, i.e. when I swipe between `Fragments`). And I populate `HashTable` in `getItem()` method of class which extends `FragmentPagerAdapter` – Eugene Sep 03 '12 at 22:19
  • I just added a `setRetainInstance(true)` to both Fragments and there is no such an exception anymore, which just confirms my assumption the references in a `Hashtable` are outdated after `orientation change`. Still quite curious how to handle the situation without `setRetainInstance(true)`. – Eugene Sep 03 '12 at 22:32
  • 3
    @siik: "which just confirms my assumption the references in a Hashtable are outdated after orientation change" -- oh, absolutely. I thought you knew that already, sorry. "Still quite curious how to handle the situation without setRetainInstance(true)" -- give unique tags to the `ListView` widgets, then use [`findViewWithTag()`](http://developer.android.com/reference/android/view/View.html#findViewWithTag(java.lang.Object)) on the `ViewPager` to find them, rather than maintaining your own separate collection. – CommonsWare Sep 03 '12 at 22:41
  • Please refer to this question - http://stackoverflow.com/questions/18425646/fragmentpageradapter-not-restoring-fragments-upon-orientation-change – Nick Sep 05 '14 at 06:51
0

I've spent some days searching for a solution for this problem, and many points was figured out:

  • use FragmentPagerAdapter instead of FragmentStatePagerAdapter

  • use FragmentStatePagerAdapter instead of FragmentPagerAdapter

  • return POSITION_NONE on getItemPosition override of FragmentPagerAdapter

  • don't use FragmentPagerAdapter if you need dynamic changes of Fragments

  • and many many many others...

In my app, like Eugene, I managed myself the instances of created fragments. I keep that in one HashMap<String,Fragment> inside some specialized class, so the fragments are never released, speeding up my app (but consuming more resources).

The problem was when I rotate my tablet (and phone). The getItem(int) wasn't called anymore for that fragment, and I couldn't change it.

I really spent many time until really found a solution, so I need share it with StackOverflow community, who helps me so many many times...

The solution for this problem, although the hard work to find it, is quite simple:

Just keep the reference to FragmentManager in the constructor of FragmentPagerAdapter extends:

public class Manager_Pager extends FragmentPagerAdapter {

    private final FragmentManager mFragmentManager;
    private final FragmentActivity mContext;

    public Manager_Pager(FragmentActivity context) {

        super( context.getSupportFragmentManager() );

        this.mContext = context;
        this.mFragmentManager = context.getSupportFragmentManager();
    }

    @Override
    public int getItemPosition( Object object ) {

// here, check if this fragment is an instance of the
//   ***FragmentClass_of_you_want_be_dynamic***

        if (object instanceof FragmentClass_of_you_want_be_dynamic) {

// if true, remove from ***FragmentManager*** and return ***POSITION_NONE***
//   to force a call to ***getItem***

            mFragmentManager.beginTransaction().remove((Fragment) object).commit();
            return POSITION_NONE;
        }

        //don't return POSITION_NONE, avoid fragment recreation.
        return super.getItemPosition(object);
    }

    @Override
    public Fragment getItem( int position ) {


        if ( position == MY_DYNAMIC_FRAGMENT_INDEX){

            Bundle args = new Bundle();
            args.putString( "anything", position );
            args.putString( "created_at", ALITEC.Utils.timeToStr() );

            return Fragment.instantiate( mContext, FragmentClass_of_you_want_be_dynamic.class.getName(), args );

        }else

        if ( position == OTHER ){

            //...

        }else

        return Fragment.instantiate( mContext, FragmentDefault.class.getName(), null );

    }
}

Thats all. And it will work like a charm...

Community
  • 1
  • 1
alijunior
  • 317
  • 1
  • 12
0

You can clear the saved instance state

    protected void onCreate(Bundle savedInstanceState) {
        clearBundle(savedInstanceState);
        super.onCreate(savedInstanceState, R.layout.activity_car);
    }


    private void clearBundle(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            savedInstanceState.remove("android:fragments");
            savedInstanceState.remove("android:support:fragments");
            savedInstanceState.remove("androidx.lifecycle.BundlableSavedStateRegistry.key");
            savedInstanceState.remove("android:lastAutofillId");
        }
    }