18

I'm developing an app with a ViewPager for lateral scrolling tables, sometimes i need to change the adapter to load a different set of tables. I've tried to do the following:

mViewPager.setAdapter(new pagerAdapterPushed(getSupportFragmentManager()));
mViewPager.getAdapter().notifyDataSetChanged();
indicator.setViewPager(mViewPager);
indicator.invalidate();
mViewPager.invalidate();

but it always reload the previous data. Isn't it possibile to change the adapter of a ViewPager?

Ste
  • 2,116
  • 7
  • 24
  • 24

6 Answers6

34

Exactly like my APP doing...

For change ViewPagerAdapter, what I did is :

  1. Clear all Fragments inside current adapter

  2. Set current adapter to null

  3. Assign new adpater for ViewPager

    /* Clear all Fragments inside current adapter */
    public class MyPagerAdapter extends FragmentPagerAdapter
    {
      private ArrayList<Fragment> fragments = new ArrayList<Fragment>();
      //...some stuff
    
      public void clearAll() //Clear all page
      {
        for(int i = 0; i < fragments.size(); i ++)
        fragMan.beginTransaction().remove(fragments.get(i)).commit();
        fragments.clear();
      }
    }
    
    /* Set current adapter to null */
    Constants.VP.removeAllViews();
    Constants.VP.setAdapter(null);
    
    /* Assign new adpater for ViewPager */
    Constants.PAGER = new MyPagerAdapter(getSupportFragmentManager());
    Constants.VP.setAdapter(Constants.PAGER);
    

Hope it helps~

Gary Chen
  • 248
  • 2
  • 14
RRTW
  • 3,160
  • 1
  • 35
  • 54
3

You need to override public int getItemPosition (Object object) in pager adapter. For every element in the viewpager, you need to return POSITION_UNCHANGED (if it is still in the pager) or POSITION_NONE (if it was removed). Otherwise the view pager won't know something has changed. I wouldn't recommend setting a new adapter, just clearing out the data in the old one.

Youngjae
  • 24,352
  • 18
  • 113
  • 198
mark.kedzierski
  • 663
  • 4
  • 10
3

I see that @RRTW way worked for some people but it did not do the trick for me (although it was close). Without commitNow() instead of regular commit() it would not work properly. I did something like that:

private fun changePagerAdapter(newAdapter: PagerAdapter) {

    val transaction = supportFragmentManager.beginTransaction()
        supportFragmentManager.fragments.forEach {
        transaction.remove(it)
    }

    transaction.commitNow()
    view_pager.adapter = newAdapter
}
Gary Chen
  • 248
  • 2
  • 14
Michał Powłoka
  • 1,424
  • 1
  • 13
  • 31
0

For those who still have issues, do like what I did. I wish it will save your time. It is in Xamarin.Android, but idea should be same in java and kotlin

Prepare your Adapter classes with ClearFragments method through an abstract class:

public abstract class BaseAdapter : FragmentStateAdapter
{
    public BaseAdapter(FragmentManager fm, Lifecycle lifecycle) : base(fm, lifecycle)
    {
        this.fm = fm;
    }
    readonly FragmentManager fm;
    
    public override int ItemCount => ITEM_COUNT;

    private readonly List<Fragment> fragments = new List<Fragment>();
    
    
    public override Fragment CreateFragment(int position)
    {
        var fragment = new MyFragment();
        fragments.Add(fragment);
        return fragment;
    }
    
    public void ClearFragments()
    {
        if (fragments.Count > 0)
        {
            var transaction = fm.BeginTransaction();
            foreach (var fragment in fragments)
            {
                transaction.Remove(fragment);
            }
            if (!fm.IsDestroyed)
            {
                transaction.CommitNow();
            }
            //see note of Dispose(disposing)
            foreach (var fragment in fragments)
            {
                fragment.Dispose();
            }
            fragments.Clear();
        }
    }
    
    private bool disposedValue = false;
    
    //Important note: Your fragment might use Context. You need to override Dispose(disposing) in your fragment class
    //and ensure you do not use Context after you dispose it here
    protected override void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            disposedValue = true;
            if (disposing)
            {
                ClearFragments();
            }
            base.Dispose(disposing);
        }
    }
}

public Adapter1 : BaseAdapter
{
    public Adapter1(FragmentManager fm, Lifecycle lifecycle) : base(fm, lifecycle)
    {
        
    }
}

public Adapter2 : BaseAdapter
{
    public Adapter2(FragmentManager fm, Lifecycle lifecycle) : base(fm, lifecycle)
    {
        
    }
}

Then when you want to change adapter, do like this:

public MainActivity : AppCompatActivity
{
    private void UseAdapter2()
    {
        var oldAdapter = viewPager.Adapter as BaseAdapter;
        oldAdapter?.ClearFragments();
        viewPager.Adapter = null;
        var adapter = new Adapter2(SupportFragmentManager, Lifecycle);
        // Do any logic that makes this adapter different from the previous one.
        viewPager.Adapter = adapter;
        oldAdapter?.Dispose();
    }
}
Ibrahim
  • 183
  • 1
  • 6
0

@ibrahim Your recommendation goes against the advice of the ViewPager2 docs. It specifically states do not use that technique for CreateFragment. You must always return a new instance of a fragment. Do not reuse from a list of fragments. See https://developer.android.com/training/animation/vp2-migration

user2153142
  • 431
  • 1
  • 4
  • 7
-5

Add this to view pager to reload for next fragment:

viewPager.setCurrentItem()
Makyen
  • 31,849
  • 12
  • 86
  • 121