Retaining Fragment instance (no configuration change) with ViewPager - android

As the title suggests, I want to retain my fragments. I have enforced my app orientation to always be in landscape mode in the manifest file so that there will be no rotations.
I have read:
Understanding Fragment's setRetainInstance(boolean)
and
Why use Fragment#setRetainInstance(boolean)?
However, I am not sure if they apply to my situation.
My project consists of a ViewPager with swipe tabs. How can I ensure that the fragments used in the ViewPager are the same ones as created when the MainActivity first starts? Would I use the same tagging method and findfragment by tag?
Additionally, where would it be best to check for the tagged fragment, else create a new fragment?
Just a side question related to ViewPager: what can I do to immediately create all fragments used in the ViewPager when the mainactivity is started, rather than to wait for a swipe event to occur?

To answer your last question view pager will by default create the fragments around the current fragment so you don't need to worry about that part. What I would recommend is let the view pager manage your fragments for you rather changing the behaviour since you might face performance issues.
SetRetainInstance simply keeps the instance of your fragment when its detached so it's up to you to assess whether you need to use it or not.. is there anything you want to maintain about that fragment? if not then do not use it, free your memory as much as possible.
For the last question, why do you want the same fragments created from the first time to be retained ? all the time? if you NEED to do that then rethink your structuring. gracefully recreate your fragments and maybe have some caching of your data on another layer if that is what you are worried about.

Related

Prevent fragment re-create in viewpager

I want to prevent fragment re-create in viewpager , in default way every time you navigate to a fragment in viewpager , android creates a new instance of that fragment.
setOffscreenPageLimit(x) can fix this problem but i don't want to load more fragments in activity's onCreate. Any idea to solve this problem and maintain fragment instance after select for next time?
There are two most used classes for View Pagers are:
FragmentPagerAdapter
FragmentStatePagerAdapter
and they look alike in many ways but not all.
What you need is FragmentPagerAdapter
This will keep the Fragment as you want and not being recreated each time as the following quote say from the official documentantion:
Implementation of PagerAdapter that represents each page as a Fragment that is persistently kept in the fragment manager as long as the user can return to the page.
And also here in the same documentation:
The fragment of each page the user visits will be kept in memory...
Although remember using this, memory will be your responsibility and you will have to manage memory of your Views check the number of pages you are having first if they are many, its not a nice idea to display 50 pages this way. Only may be less than 10 (Tabs may be its the best choice). You are good. Memory management is the only thing to take NOTE here as the documentation itself say:
This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state. For larger sets of pages, consider FragmentStatePagerAdapter.
So change your adapter to FragmentPagerAdapter and your fragments will be kept in memory not recreated.
Try setting Fragment.setRetainInstance(true). If you use FragmentPagerAdapter, this will cause an existing fragment to 'live' and not get re-created. It will to get detached and re-attached instead.
Try this, where you set the view pager adapter
viewPager.setOffscreenPageLimit(numberOfFragments)

How can I display different "screens"/views from an activity?

I am new to developing on android, finding myself somewhat confused regarding fragments and activities, and when to use the former specifically.
I want to achieve the following:
Have an activity with buttons for displaying different graphs. The selected graph should appear on screen in a panel overlaying the screen, or in fullscreen, and it should have functionality/buttons e.g. for selecting a graph timeframe.
Would creating each graph-page as fragments, routing events to the main activity be a good idea here, or should I just make a new activity for each? Or are there better options?
Cheers
I wouldn't recommend to use separate activities for this task.
The fragments are a great option for your case. You can save the state of each fragment and thus avoid recreating the graph views every time (which saves lots of CPU time if amount of data is big).
Read info about FragmentTransaciton and of course learn about working with Fragments in general. Maybe you should also try using ViewPager if you want to avoid switching fragments by yourself.
In case of using ViewPager you should use FragmentPagerAdapter (this one saves fragments for you) and you will just switch between them from your MainActivity. In each of the fragments you will implement your own graph with its own (or shared) layout file.
This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs. The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible.

Situation where you would use FragmentTransaction.replace instead or show/hide or detach/reattach?

After spending a fair bit of time figuring out that the reason my fragments chosen from a drawer layout weren`t displaying sometimes due to the choreographer skipping frames (I was using transaction.replace rather than show/hide) it made me wonder -- what are the situations where one would want to use replace rather than show/hide or detach/reattach? My problem went away when I switched to using show/hide btw.
Taken from this thread I got this on what happens when you call FragmentTransaction.replace():
Android will effectively perform a sequence of
FragmentTransaction.remove(...) (for all Fragments currently added to
that container) and FragmentTransaction.add(...) (for your supplied
Fragment). Removing a Fragment from the FragmentManager will cause the
Fragment to be destroyed and its state will no longer be managed. Most
noticeably, when you re-add the Fragment all of the views will have
been reset. Note: since you are reusing the same Fragment instance,
the Fragment will still keep the value any instance variables.
and from this thread I got that it is probably better to show/hide rather than replace if you plan on using that fragment again. My question is, in which situations do you use FragmentTransaction.Replace()? The only place I could see it really being useful is for something you know you won`t need again, kind of like a dialog picker with options but I use dialog fragments for those situations.
Does anyone use FragmentTransaction.replace regularly, and if so, why did you choose that over another method? Cheers
It maybe useful, for example, when implementing a deep fragments hierarchy in Multi-pane pattern (when click on item in the right fragment moves it to the position of the left).
Also, since hiding a Fragment keeps it in FragmentManager, it maybe expensive if you have a heavy content in it or hide multiple instances. Calling remove() or replace() and properly saving fragment's state is more Android-way, I think.

Getting data (asynchronous) and populating ViewPager's fragments

I have an activity with a ViewPager with a variable number of fragments (tabs).
Upon start the activity checks if the associated (complex) data has been loaded. If it hasn't it shows a progress bar view and starts an AsyncTask which fetches the data. Depending on the data the activity creates a number of fragments (tabs) and gives each fragment a sub set of the data.
I currently hold references to the fragments (I know that it is discouraged) and I run into all sorts of problems when the fragments gets reused - I'm giving the data to the wrong instance of the fragment.
So, is there an "android way" of solving this problem?
I run into all sorts of problems when the fragments gets reused
Fragments usually don't get reused in a ViewPager. This is not like an AdapterView where rows get recycled. Using FragmentPagerAdapter or FragmentStatePagerAdapter, a fragment represents one and only one page.
I re-instantiate the ViewPager each time (but the fragments get reused?)
Ah. That's a slightly different problem.
The stock implementations of FragmentPagerAdapter and FragmentStatePagerAdapter make a couple of assumptions:
They are in complete control over the fragments, particularly in terms of running the transactions to add and remove them from the UI
That those fragments will only ever be used by one "logical" ViewPager (IOW, recreating that ViewPager for a configuration change is fine, but that' pretty much it)
Complicating matters is that these adapters store the fragments under certain tags, and therefore if those fragments still exist in those tags, those existing ones will get used, instead of new ones being created.
So, is there an "android way" of solving this problem?
It's unclear from your question why you even need to "re-instantiate the ViewPager". I'm assuming that this is tied to some sort of refresh operation, or something else that is forcing you to go through the process described in your second paragraph.
You could give my ArrayPagerAdpater a try, as it is friendlier about external agents mucking about with the fragments. Since you control the fragments' tags, you can always be certain that you are working with the right fragment -- rather than caching them yourself, just retrieve the right one and manipulate it.

Programmatically removing a ViewPager, when are (or how can I ensure) contained Fragments are destroyed?

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.
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().

Categories

Resources