6

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

Cheborra
  • 2,557
  • 4
  • 21
  • 38
  • You can use AsyncTask with a SERIAL executor. http://developer.android.com/reference/android/os/AsyncTask.html#SERIAL_EXECUTOR You'll have 1 thing running at a time but it can queue up. Just make sure you call cancel() in your onStop do you don't run tasks you no longer need – whizzle May 28 '14 at 04:17
  • You can still use the `OnPageChangeListener`, but to access the fragment you'll need a different approach. Have a look at this question http://stackoverflow.com/questions/7379165/update-data-in-listfragment-as-part-of-viewpager?lq=1 – user May 28 '14 at 04:41

4 Answers4

9

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);
    }
}
sergej shafarenka
  • 19,464
  • 6
  • 62
  • 82
  • did you use it in combination with FragmentStatePagerAdapter (not just regular adapter) ? Because I'm testing it right now and is not working (not working means, only calling it on "false") – Cheborra May 27 '14 at 20:52
  • Just double checked. I replaced FragmentPageAdapter with FragmentStatePageAdapter. setUserVisibleHint() works just fine. Visible fragment receives "true". Try to use very simple empty fragments with overwritten setUserVisibleHint(boolean isVisibleToUser) method. You will see the method gets called with "true" for visible fragment. I will update the answer with an example. – sergej shafarenka May 27 '14 at 21:02
  • turns out that the problem was that I was wrapping the Adapter around another Adapter (Infinite Adapter), and that wrapper wasn't implementing the "setPrimaryItem()" method, and thus, the "setUserVisibleHint(true)" was never being call. Anyways, will accept this answer (and give bounty) because it pointed me in the right direction. – Cheborra May 28 '14 at 16:24
  • I suspected that the issue was somewhere in the coding around, that's why I gave that simple example which definitely worked. – sergej shafarenka May 28 '14 at 18:24
1

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.

x-code
  • 2,902
  • 1
  • 16
  • 19
1

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
      }
  });
rana
  • 1,406
  • 3
  • 18
  • 35
0

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.

ILovemyPoncho
  • 2,622
  • 2
  • 19
  • 33