5

Having searched regarding this issue beforehand, I can find many discussions regarding dynamically adding and removing selected Fragments from a ViewPager. What I'm actually concerned about here however is how I can programmatically remove an entire ViewPager 'cleanly' from its containing ViewGroup, when that ViewPager has been used to display Fragments via a FragmentPagerAdapter, and ensure that the contained Fragments are destroyed properly.

To expand on the question a bit more, I have a landscape two-pane layout where a selection is made from a list within a Fragment on the left-hand-side, and chosen content is then placed on the right within a FrameLayout. The key thing is that the content may or may not be paginated. Therefore, the content must either be displayed in a ViewPager format, or if it is not paginated then it shall be represented by a single Fragment directly.

To show a single Fragment, I simply perform a FragmentTransaction as you normally would in order to place the Fragment into the FrameLayout container. If on the other hand it's paginated content to be shown, then instead I create a ViewPager and add it as a child of the FrameLayout.

When I need to change the content, then if the previous content was a stand-alone Fragment then I can simply remove it via FragmentTransaction .remove(). When I do this, the Fragment goes through the onPause() ... onDestroy() cycle as expected. If the previous content was a ViewPager then I remove it from the FrameLayout using .removeAllViews(). Here I come to the problem: I don't see any of the onPause() ... onDestroy() methods being called in any of the Fragments that were held within that ViewPager via the FragmentPagerAdapter.

From a user point of view, the application works fine. After several rounds of ViewPager being removed, I can see the GC reclaiming memory. However, I don't like the fact that those Fragments' end of life methods aren't called as I can't do any cleanup within them, and it just doesn't seem 'right'.

Is there a method I can hook into in order to remove the ViewPager's Fragments when the ViewPager is detached from its parent, perhaps? In other words, when I know that the ViewGroup is no longer in used, I would perform FragmentTransactions somewhere (perhaps in the FragmentPagerAdapter) to remove those Fragments.

Alternatively, I realise that I could just keep the ViewPager on the right permanently, and dynamically swap the Fragments within it. Of course it simply would not matter that at certain times it would only hold one page. If this would be a better way to go then I shall refactor my code to do this, but I would appreciate opinions.

Trevor
  • 10,903
  • 5
  • 61
  • 84

1 Answers1

12

However, I don't like the fact that those Fragments' end of life methods aren't called as I can't do any cleanup within them, and it just doesn't seem 'right'.

They should get cleaned up when the activity is destroyed, if that is not too late for you (e.g., heap issues).

In other words, when I know that the ViewGroup is no longer in used, I would perform FragmentTransactions somewhere (perhaps in the FragmentPagerAdapter) to remove those Fragments.

You did not execute the transactions to put the fragments there. Hence, you cannot readily execute the transactions to remove the fragments. If you switch to FragmentStatePagerAdapter, and call setAdapter(null), it should cause all existing fragments in the pager to be destroyed, by my reading of the source code. FragmentPagerAdapter never uses remove(), but FragmentStatePagerAdapter does, from its destroyItem() method, and all extant fragments are destroyed via destroyItem() when a new adapter (or null) is supplied to setAdapter().

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Thank you. A solution I came to just now is to use `FragmentManager` within `destroyItem` to remove the `Fragment`. I also return `PagerAdapter.POSITION_NONE` from `getItemPosition` and call `notifyDataSetChanged()`. By doing this prior to removing the `ViewPager`, the `Fragment`s' `onDestroy` is called and I'm happy. Do you see any issues with this? The reason for using `FragmentPagerAdapter` is because I want all `Fragment`s (a small number) to stay around, because my UI requires that user data is persisted from all fragments in response to a single accept button action. – Trevor Oct 07 '12 at 11:35
  • 1
    @Trevor: "Do you see any issues with this?" -- that should be OK, though be careful when taking on any updates to the Android Support package. For all we know, they'll change the behavior such that Android also tries destroying the fragments, and you may wind up with errors and have to back out your own destroy logic. – CommonsWare Oct 07 '12 at 11:39
  • 1
    Thank you again, and understood. Seems what I have now then is best of both worlds: Using `FragmentPagerAdapter` means that paging away from a `Fragment` doesn't destroy it, but by extending `destroyItem` to remove the fragment just like `FragmentStatePagerAdapter` does, I can remove Fragments when I want. (I appreciate requiring all attached fragments to stay around so that I can request them to persist user-input data at once could be frowned upon, but otherwise I would have to refactor so that all Fragments passed this data to the main Activity, which I'd rather avoid.) – Trevor Oct 07 '12 at 11:47
  • @Trevor can u please provide the code of the solution ? I would be grateful thanks – N Jay Aug 15 '13 at 09:55