Replacing Fragments in ViewPager is a question that has been asked many times but most answers are hacks, broken/not complete or quite old. This example is working really well - https://github.com/danilao/fragments-viewpager-example but it actually uses an empty root fragment and adds the real fragment on createView.
Is there any other better way to avoid having to use a root frame?
The correct approach would be to override your PagerAdapter's getItemPosition() to update your PagerAdapter with the new Fragment:
Called when the host view is attempting to determine if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.
In this case, pages that haven't changed return PagerAdapter.POSITION_UNCHANGED, the Fragment that is removed returns PagerAdapter.POSITION_NONE, and the new Fragment gets its new position.
You'll change the underlying data in your PagerAdapter, then call notifyDataSetChanged(). This is what triggers the ViewPager to call getItemPosition() for each visible Fragment, replacing or moving them as needed.
Your example uses the right technique (child fragments inside root fragment) but the wrong way: for managing child fragments inside the root fragment, it must use the child fragment manager using getChildFragmentManager().
Related
I have 3 fragments in the ViewPager.
PersonalFragment
CropFragment
NotesFragment
Every fragment has a RecyclerView and I was adding items using a dialog in it. However, when I go to the last fragment (i.e. NoteFragment), the first fragment's (i.e. PersonalFragment) RecyclerView gets empty (all items are removed). The same thing happens when I come back to the first fragment, the last fragment's RecyclerView resets (all items are removed).
It is difficult to understand without seeing some of your code which is related. I think you might have implemented the onResume function incorrectly. Each time you go to the third fragment in your ViewPager, the first Fragment needs to be reinitialized and you need to take care of this in your onResume function.
However, another workaround for your problem is setting the off-screen page limit of the ViewPager like the following.
viewPager.setOffscreenPageLimit(2);
Hope that helps.
I have a simple activity with two fragment: main (contains a list of items) and detail. Details screen is a fragment, which is displayed when an item is clicked in main list. DetailFragment is being shown using replace().
After navigating back from detail screen I would like to have exact same scroll position in RecyclerView that I left it with.
As long as replace() initiates onDestroyView() and onCreateView() cycle to happen for MainFragment, after coming back from DetailFragment the RecyclerView has a scroll position of 0 (at the top), because this is a brand new RecyclerView which has no connection to the one that was there before leaving to DetailFragment.
replace()ing fragment does not initiate onSaveInstanceState() to be called, that's why using techniques outlined here are not applicable.
I wonder what is the correct way of handling this use-case?
Of course I can save the position on onDestroyView() and later manually scroll to that position, but maybe I'm missing some obvious solution?
An answer, that will propose to use add() instead of replace() is not welcome.
I've had RecyclerView declared as a child of NestedScrollView. This was the issue.
When I got rid of NestedScrollView everything was handled automatically.
As onSaveInstanceState() is not possible, try using ViewModel to save the required data.
When you declare and set the adapter to your first recyclerview, try having a call back to your actvity passing the adapter instance , where in your activity you can try saving the adapter instance or any required data in the ViewModel of your activity.
Once you come back from your details fragment you can get the saved instance of your reycycler data from the activity`s ViewModel.
I have a ViewPager. It has Fragments. When those Fragments are created, they load data from the server. When the user selects a menu item, I want to tell those fragments to filter their data without reloading from the server.
To do this, I have a subclass of FragmentPagerAdapter which keeps a List to hold a reference to each Fragment as it is created. When the menu item is selected, I simply iterate over that collection and call a method on each Fragment.
The problem arises when the device is rotated. The framework automatically restores the fragments to their parent view, without calling the getItem method of my adapter, meaning that my List is empty.
How can I either repopulate that List when the device is rotated, or obtain a list of all of the ViewPager's child Fragments without keeping a list?
EDIT: It may be important to note that my ViewPager lives in a Fragment, and that Fragment gets hot swapped via a side navigation menu. In other words, my ViewPager is not owned by an Activity, and there are some known issues about ViewPager not cleaning up its child fragments correctly which may be exacerbating the problem.
Check this link Get Current visible fragment in ViewPager
The author basically suggests 2 similar approaches which are to attach tags on fragment creation and keep track of it.
getSupportFragmentManager().beginTransaction().add(myFragment, "Some Tag").commit();
Then retrieve the tag like so:
int index = mViewPager.getCurrentItem();
String tag = mPageReferenceMap.get(index);
Fragment myFragment = getSupportFragmentManager().findFragmentByTag(tag);
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.
Is it possible to remove a single Fragment at a position from a ViewPager without causing the whole ViewPager to redraw, i.e., destroy only 1 fragment without destroying the others in the ViewPager?
Using the example code on this page: FragmentStatePagerAdapter, calling notifyDataSetChanged() after decrementing NUM_ITEMS, causes all Fragments to get destroyed and drawn again except the last due to overriding getItemPosition() to return POSITION_NONE. How can this be avoided?
Use activityfragment from support jar and using getFragmentManager API, manually add remove fragments at the runtime.
Only issue here is you will have to keep track of each of the fragment references.