ViewPager2 - fragment layout disappears after multiple swipes via tabs - android

I'm using ViewPager2 which is hosted inside a fragment. On the initial opening of ViewPager2 fragment host everything is loaded and displayed correctly, but when I swipe multiple times (via TabLayout) to the last fragment and go back to the first fragment, its layout is empty/white screen.
The adapter which I'm using is FragmentStateAdapter. Also, I set offscreenPageLimit to a constant value (which is in most cases less than the size of the fragments list in ViewPager2).
This is constructor of my custom FragmentStateAdapter:
pagerAdapter = MyCustomPagerAdapter(requireContext(), childFragmentManager, viewLifecycleOwner.lifecycle)
Since ViewPager2 is using RecyclerView internally, maybe there is some problem with recycling fragments when there are not visible (also considering offscreenPageLimit value).

Problem was in by viewBinding() method for getting binding object.
After using standard way of creating binding object everything is working OK.

Related

Viewpager not clearing fragments

I am having this problem with nested viewpagers.
I have two levels of viewpager. Lets call it levelOneViewPager inside which I have one more viewpager, let's call this levelTwoViewPager.
The Activity loads the data for levelOneViewPager and sets the adapter.
The levelOneViewPager loads data for levelTwoViewPager in onViewCreated and sets the adapter.
Everything works fine when the levelOneViewPager loads data for first time.
Now here is the problem.
First page of levelOneViewPager is loaded, I move to 4th page and come back to first page and the app crashes.
Reason, now first page of levelOneViewPager is recreated and is loading the data, even before it sets adapter, the fragments of levelTwoViewPager are created and is accessing some data in the parent fragment which is not available yet.
How is this happening? I haven't set the adapter to levelTwoViewPager yet but fragments are created for it.
(I tried using both FragmentPagerAdapter and FragmentStatePagerAdapter, it's the same behaviour)
add offscreenpage limit to parent viewpager this should fix the issue

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

FragmentStatePagerAdapter performance issue

I have implemented an activity that displays fragments in a viewpager. The activity initially displays 5 fragments of a given type (type A). Each type A fragment contains a list of information. When a button is pressed, the fragments are replaced with another tpye of fragments, type B (i.e., fragments that use a different layout and display different information are loaded).
I have implemented this with a ViewPager and a custom FragmentStatePagerAdapter. The custom FragmentStatePagerAdapter overrides the getCount, instantiateItem, getItem, getItemPosition, etc. functions and all works fine. When I press the button the views switch from fragment type A to fragment type B. In order to do this I am using a replace fragment transaction and I also call notifyDataSetChanged on the adapter. The switch from type A to type B fragments is accomplished by checking which type of fragment I need to create in the adapter's getItem function. The problem is that the switching process (i.e., changing from fragment type A to fragment type B and vice versa) takes some time (around 2 seconds) - this delay is quite noticeable and annoying.
This occurs because the adapter's getItemPosition function returns POSITION_NONE. When POSITION_NONE is returned, the viewPager recreates the fragment. Furthermore, when the replace transaction is executed, all items of the adapter are removed and all new ones are recreated. The same happens when I switch back to fragments type A.
Does anyone know if there is a better or faster way to do this? Using a FragmentPagerAdapter should not be a good solution since the number of fragments in the viewPager could grow.
I have commented out the updating of the fragments and the problem is gone so the problem is the time it takes to update the fragments. Is there a way to update the fragments asynchronously? That should take care of the problem.
The ViewPager instatiates by default a minimun count of Fragments. In general the left and right one of the current Fragment. So you don't need to care about the total number of Fragments.
You can control the total number with the ViewPager.setOffScreenPageLimit() method.
Also a AsyncTask seems to be a good idea to manage the intensiv work of your Fragments.

Mixing layout values each other when use same layout for different fragments in ViewPager

In my Android application there are two fragments called FragmentA and FragmentB. I added both fragments to a ViewPager using a FragmentAdapter So when I run the application I'm getting a unexpected layout (both fragment layouts are mixed together). Whenever I tried changing the currentItem of ViewPager by swiping for each time the layout is mixing each other.
My question: Is there any problem if we use same layout to different fragments in a ViewPager?
I found the answer.
There is no problem if you use getView() function of Fragment to refer the current root view of a fragment. So that you can get exact views in the Fragment. You can use like this(getView().findViewByid(...);
But there is a problem if you use activity reference to refer views in the fragment. Because all views in each fragment which is using same layout xml has same id. So if you change the value of a view in a fragment that will reflect in other fragments which are using the same layout in ViewPager. One more thing, think like this, when the activity created, all the fragments in the ViewPager of that activity will also be created and runs in background. So if you change the value of a view, android will return 1st view which is having the same id. Android knows only id of views. So always refer views of fragment using it root view (getView()).

Categories

Resources