Keeping PageView Fragment's View from disposal - android

I have an Android Application implementing a PageView with Tabs found here: https://github.com/sbpc/ajavacc The PageView loads its Fragments and displays everything properly; but occasionally the individual Fragments become disposed. Is there a way to either store the Fragment's state, prevent the pages from being disposed; or do I need to save the values which construct each page to a file?

The class MainPagerAdapter is already using FragmentPagerAdapter which keeps the created Fragments in memory. You can go one step further and add the method setRetainInstance(true) to each Fragment in the ViewPager. This should work. You can read more about this here.

Related

How to go directly to a specific fragment using ViewPager2

I am trying to create an app using Viewpager2 that allows scrolling between fragments similar to below image but WITHOUT the tablayout at the top.
So if you scroll left and right you would swtich between the fragments. Thats fine and dandy.
However my final app would have more than 5 tabs so i dont want the tab layout listed on top. Instead I would have a menu option that would let user choose which fragment to view.
I cannot figure out how to get the viewpager to go to a specific fragment directly (eg. from 2nd fragment to the 5th fragment), and when navigating between fragments I do not want a fragment to start from scratch if if was created already.
I am using FragmentStateAdapter with my ViewPager2. Should i be using another adapter for this? Any ideas woule be greatful.
The easiest way to implement this feature is to define your ViewPager as static in your activity (so you can access it from anywhere in your application, including fragments). You can make it public, or you can make it private and create a getter for it.
private static ViewPager mViewPager; // This is the global variable
public static ViewPager getViewPager() { return mViewpager; } // This is the getter
Now lets say we have an activity MainActivity which contains the ViewPager. Each fragment inside this ViewPager contains a button which should control the page (doesn't have to be in the ViewPager, could be anywhere as long as it has a reference to your Activity and that Activity is still running, so don't start another one).
ViewPager contains the method
setCurrentItem(int item, boolean smoothScroll)
(smoothScroll is optional and is just for animation purposes)
Now you can do:
MainActivity.getViewPager().setCurrentItem(itemNr);
Or if you want a fancy scroll animation:
MainActivity.getViewPager().setCurrentItem(itemNr, true);
Now your second question / issue was that you mentioned you don't want fragments to get recreated. In that case I think it is better to use FragmentPagerAdapter, which maintains your fragments. You can call the following method to set a limit to these pages:
mViewPager.setOffscreenPageLimit(LauncherViewPageAdapter.NUMBER_OF_PAGES); //NUMBER_OF_PAGES was just a static constant in my LauncherViewPageAdapter class which extended ViewPageAdapter.
Note: Default this limit is 3, but you really don't have to worry about performance. I implemented 4 pages / fragments (widgets, tiles, app list, application settings) without any memory issues (even on older devices). Just don't put them full with bitmaps / heavy memory usage stuff.
I'm currently working on a launcher application which also uses ViewPager, so feel free to reply with a question about the ViewPager, since I solved the basic issues I had.
You can make use of setCurrentItem method of your ViewPager2. Just pass the index your wish to navigate to starting from 0. As for not wanting the fragments to start from scratch simply use setOffscreenPageLimit method of viewpager2 and set it to like 4 in case you have 5 pages so that all 5 pages stay in memory. It actually defeats the purpose of FragmentStateAdapter and ViewPager2 though.

Android ViewPager with all fragments of same class without transaction

I know this question has been asked many times before but I couldn't find an explanatory answer.
I want to have a single MyFragment.java and display it for all 3 pages of ViewPager.
But when I try to create a fragment with this method:
new MyFragment()
and add it to the ViewPager I get this error:
java.lang.IllegalStateException: Fragment already added
at androidx.fragment.app.Fragment.setInitialSavedState(Fragment.java:679)
at androidx.viewpager2.adapter.FragmentStateAdapter.ensureFragment(FragmentStateAdapter.java:269)
at androidx.viewpager2.adapter.FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:175)
at androidx.viewpager2.adapter.FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:67)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
I'd like to know if there is any possibility to add the same fragment to ViewPager.
Possible applications of it include
A web browser having WebView fragments in Viewpager
Why exactly do we need to use singleton fragment?
Why can't we treat fragments as recycler items which can be multiple instantiated?
Even the viewpager2 is becoming more like RecyclerView.
If it is impossible to instantiate fragments repeatedly and add them
to the same ViewPager then are there any other ways except replacing
fragments in ViewPager adapter with normal inflated views? Would that
be a tolerable solution?
You get this error because when you add the fragment you don't put a distinct tag on it, so it checks whether the fragment is already added by its class. And the viewpager stores some pages in memory, so the previous fragment even though is no longer in view, is still in the memory.
When you add the fragment, give it a distinct tag

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)

Retaining Fragment instance (no configuration change) with ViewPager

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.

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