Cheborra
Cheborra

Reputation: 2687

FragmentStatePagerAdapter onPageSelected event

I have a FragmentStatePagerAdapter with 6 fragments inside.

Each Fragment connect's to the server and loads data.

Right now, the server connection is being done in Fragment.onStart(), that means that at any moment, I have 3 http requests going (the selected Fragment, and one to each side).

What I want is to have only one connection at the time, so I figure to use

 viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener()
 { 
    @Override
    public void onPageSelected(final int position)
    {
        CustomFragment fragment = (CustomFragment) myFragmentStatePagerAdapter.getItem(position);

        fragment.onSelected();//do stuff in here
    }
 });

The thing is, getItem() returns a new instance of the fragment, not yet added to the manager (and thus, not yet view created, etc).

Also, I've tried setUserVisibleHint(boolean isVisibleToUser) but is not being call on visible, only on isVisibleToUser = false

So, how to achieve a "onPageSelected()" event for the Fragment?

Thanks in advance

Upvotes: 6

Views: 9283

Answers (4)

rana
rana

Reputation: 1862

Late to the train .Personally none of this approach is my favorite . They are hacky. Best way is using Adapters setPrimaryItem(ViewGroup container, int position, Object object)

Inside setPrimaryItem' save the current Fragment and use it insideonPageSelected(final int position)`

Code sample goes some thing like this ,

  @Override
  public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);
    currentFragment = (ProductListFragment) object;
   }

  // Inside view pager listener use the currently visible fragment
  viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener()
   { 
       @Override
       public void onPageSelected(final int position)
      {
         currentFragment.onSelected(); // do your thing man
      }
  });

Upvotes: 1

ILovemyPoncho
ILovemyPoncho

Reputation: 2792

In your FragmentStatePagerAdapter use a HashMap to hold the created Fragments, then you can access them later from your viewpager container (activity or fragment):

public class CustomFragmentPagerAdapter extends FragmentStatePagerAdapter {

    private HashMap<Integer, CustomFragment> fragMap = 
        new HashMap<Integer, CustomFragment>();

    ...

    @Override
    public Fragment getItem(int position) {
        CustomFragment frag = new CustomFragment();
        ...
        fragMap.put(position, frag);
        return frag;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragMap.remove(position);
    }

    public CustomFragment getFragment(int position) {
        return fragMap.get(position);
    }
    ...
}

Now, in your viewpager container(activity or fragment):

private int lastPage = 0;

...

viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {

        CustomFragment fragment;
        int currentPage = viewPager.getCurrentItem();

        /* connect the current fragment and disconnect the previous one, in case
           you actually need to disconnect it from the server, 
           otherwise just connect the current fragment.*/
        if(currentPage > lastPage) {
            fragment = yourPagerAdapter.getFragment(currentPage - 1);
            fragment.disconnect();
            fragment = yourPagerAdapter.getFragment(currentPage);
            fragment.connect();
        } else {
            fragment = yourPagerAdapter.getFragment(currentPage + 1);
            fragment.disconnect();
            fragment = yourPagerAdapter.getFragment(currentPage);
            fragment.connect();
        }
        lastPage = currentPage;
    }
});

Since the onPageChangeListener is not called when the app is started, you will need to connect the first fragment. The important thing is that you will have a reference to the fragments returned by getItem() using yourPagerAdapter.getFragment(currentPage).

Edit:

I think this may be a better logic for onPageSelected() callback method:

viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {

            CustomFragment fragment;
            int currentPage = viewPager.getCurrentItem();
            Set<Integer> myKeySet = yourPagerAdapter.getKeys();
            for(Integer key : myKeySet) {
                if(key == currentPage) {
                    fragment = yourPagerAdapter.getFragment(key);
                    fragment.connect();
                } else {
                    fragment = yourPagerAdapter.getFragment(key);
                    fragment.disconnect();
                }
            }

        }
    });

But you need to implement this methd on FragmentStatePagerAdapter:

public Set<Integer> getKeys() {
    return fragMap.keySet();
} 

and get rid of lastPage data member.

Upvotes: 0

x-code
x-code

Reputation: 3000

What has worked for me is, in my pager adapter's getItem method, to save in a list my own reference to the custom fragment I return. The contract that both fragment pager adapter and fragment state pager adapter seem to follow means that the last fragment returned by getItem(i) will be the correct fragment to refresh in the onPageSelected(i) method.

I have only tested this technique with a fragment pager adapter and a reasonably small number of tabs. With a state pager adapter some testing would be needed to see if holding references to fragments causes memory use to increase.

Upvotes: 1

sergej shafarenka
sergej shafarenka

Reputation: 20406

The best solution for you would be to override setUserVisibleHint(). Make sure you extend FragmentPagerAdapter. From its source code you can see it calls setUserVisibleHint(true) for visible fragments too. I use it all the time and it works well.

// adapter
public static class PlayerAdapter extends FragmentStatePagerAdapter {
    public PlayerAdapter(FragmentManager fm) {
        super(fm);
    }

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

    @Override
    public Fragment getItem(int position) {
        return new SimpleFragment();
    }
}

// fragment
public class SimpleFragment extends Fragment {

    private static final String TAG = BookControlsFragment.class.getSimpleName();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_simple, container, false);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.d(TAG, "setUserVisibleHint: " + isVisibleToUser);
    }
}

Upvotes: 8

Related Questions