ViewPager and fragments — what's the right way to store fragment's state?

后端 未结 11 1360
遇见更好的自我
遇见更好的自我 2020-11-22 02:21

Fragments seem to be very nice for separation of UI logic into some modules. But along with ViewPager its lifecycle is still misty to me. So Guru thoughts are b

11条回答
  •  孤街浪徒
    2020-11-22 02:35

    I want to offer a solution that expands on antonyt's wonderful answer and mention of overriding FragmentPageAdapter.instantiateItem(View, int) to save references to created Fragments so you can do work on them later. This should also work with FragmentStatePagerAdapter; see notes for details.


    Here's a simple example of how to get a reference to the Fragments returned by FragmentPagerAdapter that doesn't rely on the internal tags set on the Fragments. The key is to override instantiateItem() and save references in there instead of in getItem().

    public class SomeActivity extends Activity {
        private FragmentA m1stFragment;
        private FragmentB m2ndFragment;
    
        // other code in your Activity...
    
        private class CustomPagerAdapter extends FragmentPagerAdapter {
            // other code in your custom FragmentPagerAdapter...
    
            public CustomPagerAdapter(FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public Fragment getItem(int position) {
                // Do NOT try to save references to the Fragments in getItem(),
                // because getItem() is not always called. If the Fragment
                // was already created then it will be retrieved from the FragmentManger
                // and not here (i.e. getItem() won't be called again).
                switch (position) {
                    case 0:
                        return new FragmentA();
                    case 1:
                        return new FragmentB();
                    default:
                        // This should never happen. Always account for each position above
                        return null;
                }
            }
    
            // Here we can finally safely save a reference to the created
            // Fragment, no matter where it came from (either getItem() or
            // FragmentManger). Simply save the returned Fragment from
            // super.instantiateItem() into an appropriate reference depending
            // on the ViewPager position.
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
                // save the appropriate reference depending on position
                switch (position) {
                    case 0:
                        m1stFragment = (FragmentA) createdFragment;
                        break;
                    case 1:
                        m2ndFragment = (FragmentB) createdFragment;
                        break;
                }
                return createdFragment;
            }
        }
    
        public void someMethod() {
            // do work on the referenced Fragments, but first check if they
            // even exist yet, otherwise you'll get an NPE.
    
            if (m1stFragment != null) {
                // m1stFragment.doWork();
            }
    
            if (m2ndFragment != null) {
                // m2ndFragment.doSomeWorkToo();
            }
        }
    }
    

    or if you prefer to work with tags instead of class member variables/references to the Fragments you can also grab the tags set by FragmentPagerAdapter in the same manner: NOTE: this doesn't apply to FragmentStatePagerAdapter since it doesn't set tags when creating its Fragments.

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
        // get the tags set by FragmentPagerAdapter
        switch (position) {
            case 0:
                String firstTag = createdFragment.getTag();
                break;
            case 1:
                String secondTag = createdFragment.getTag();
                break;
        }
        // ... save the tags somewhere so you can reference them later
        return createdFragment;
    }
    

    Note that this method does NOT rely on mimicking the internal tag set by FragmentPagerAdapter and instead uses proper APIs for retrieving them. This way even if the tag changes in future versions of the SupportLibrary you'll still be safe.


    Don't forget that depending on the design of your Activity, the Fragments you're trying to work on may or may not exist yet, so you have to account for that by doing null checks before using your references.

    Also, if instead you're working with FragmentStatePagerAdapter, then you don't want to keep hard references to your Fragments because you might have many of them and hard references would unnecessarily keep them in memory. Instead save the Fragment references in WeakReference variables instead of standard ones. Like this:

    WeakReference m1stFragment = new WeakReference(createdFragment);
    // ...and access them like so
    Fragment firstFragment = m1stFragment.get();
    if (firstFragment != null) {
        // reference hasn't been cleared yet; do work...
    }
    

提交回复
热议问题