0

Problem

I was looking over the solution to this SO post from a while back. This solution makes sense to me. However, I'm having issues as to when I should try accessing the fragment that is currently being displayed. I'm using the same setup as the linked question: a ViewPager with a FragmentStatePagerAdapter. I've implemented the adjustments to my FragmentStatePagerAdapter as the solution suggests:

private class MyPagerAdapter extends FragmentStatePagerAdapter {
        SparseArray<Fragment> registeredFragments = new SparseArray<>();

        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }

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

        @Override
        public Fragment getItem(int position) {
            mCursor.moveToPosition(position);
            return ArticleDetailFragment.newInstance(mCursor.getLong(ArticleLoader.Query._ID));
        }

        @Override
        public int getCount() {
            return (mCursor != null) ? mCursor.getCount() : 0;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment fragment = (Fragment) super.instantiateItem(container, position);
            registeredFragments.put(position, fragment);

            return fragment;
        }

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

        public Fragment getRegisteredFragment(int position) {
            return registeredFragments.get(position);
        }
    }

The issue comes when trying to access the current fragment. I tried accessing it within onResume():

@Override
protected void onResume() {
    super.onResume();

    ArticleDetailFragment fragment = (ArticleDetailFragment)
            mPagerAdapter.getRegisteredFragment(mPager.getCurrentItem());

    mCallback = (FragmentCallback) fragment;
}

...But my fragment is always null. This is because onResume() is called prior to instantiateItem() is called within my adapter. The fragment is null because no fragments have been added yet to registeredFragments within the adapter.

Question

I was hoping to get some guidance on which activity lifecycle method I should use where instantiateItems() would have already been called and I'd have actual fragments to work with. As always, any help on this would be appreciated.

Additional Background

What I'm really trying to achieve with this is to get a reference to my fragment so that I can instantiate a callback interface that I've defined in my activity. I'm hoping to use this interface to communicate with the fragment that's currently being displayed within the ViewPager. However, in order to be able to do that, I first need a reference to the current fragment.

coolDude
  • 647
  • 2
  • 11
  • 27
  • cant you simply pass a list of instantiated Fragments to the MyPagerAdapter constructor and fill the registeredFragments.put(position, fragment); in instantiateItem method with that array. then when you have reference to those fragments you can directly access them – Mohit Feb 16 '18 at 04:43

2 Answers2

2

The general rule of them should be

Don't call me. I'll call you.

This means you should let the Fragment notify others when it is available and what data it is willing to share. You can do this with an interface and/or callback method of some kind. Communicating with Other Fragments from the Android docs shows how to do this for communication between two Fragments. The same concepts apply since this communication goes through the parent activity. If you want to communicate with the activity, then you skip the step about sending the message to the other fragment.

The basic idea is that you should give the Fragment a callback instead of using the Fragment as a callback.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • That definitely makes sense. What I'm trying to do is animate a button in my fragment when the pages are changed in my `ViewPager`. The button is in my fragment but the pages managed by the activity. Based on what you're recommending, I'd need 2 interfaces: one from fragment to activity and another in the opposite direction. Is that correct? It seems redundant though... – coolDude Feb 16 '18 at 14:16
0

While InstantiateItems() is typically called by getItem() for getting new View and ideally Fragment is created in getItem() method, so you need to use put() method in getItem() as in

 @Override
 public Fragment getItem(int position) {
      mCursor.moveToPosition(position);
      Fragment createdFragment = ArticleDetailFragment.newInstance(mCursor.getLong(ArticleLoader.Query._ID));
      registeredFragments.put(position, createdFragment );
      return createdFragment;
 } // also remove InstantiateItems() method
Rajan Kali
  • 12,627
  • 3
  • 25
  • 37