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.
Related
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().
I am using a FragmentStatePagerAdapter in combination with a view pager, which is nested in a fragment on Android. Thus, I am using the ChildFragmentManager in combination with the FragmentStatePagerAdapter to create my fragments.
So far so good, but here is the problem:
If I open a different Activity without finishing the activity that hosts the fragment with the view pager and afterwards resume it, the fragment that was selected in the ViewPager is empty. If I switch the fragment in the ViewPager before I open the different Activity, this issue does not appear. After returning and seeing the empty Fragment in the ViewPager, I am still able to switch the next Fragments, but if I try to switch back to the empty Fragment, I get a NullPointerException. This issue does not occur if I switch between the Fragment hosting the ViewPager and a different Fragment or if I change orientations.
If I use the FragmentManager instead of the ChildFragmentManager, I do not have this issue when switching to a different Activity, but it will then occur if I switch between the hosting and a different fragment or if I change the orientation.
So here's what I already tried without any success:
* Use FragmentStatePagerAdapter adapter and FragmentPagerAdapter
* Use setRetainedInstance(true and false)
* Use getChildFragmentManager() and getFragmentManager()
* Override getItemPosition() return POSTION_NONE and use notifyDataChanged() in onResume()
The only thing that is currently doing the trick is recreating the PagerAdapter in onResume() and re-initializing the ViewPager, but I think that's really bad practice as everything needs to be recreated.
So is there somebody, who can explain to me how a correct implementation should look like, or why the fragments can become empty (they are not null, I checked that). I am searching for a more elegant solution rather than recreating everything each time I resume my activity.
Any help is really appreciated.
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.
I am implementing Tab Layout with Swipeable Views in android. For implementation I had followed
AndroidHive
and
Tabs and swipe views.
But with both, I am facing the same problem. I am having 3 fragments but when my application run, onCreateView of 1st and 2nd Fragment called instead of only of 1st fragment's. When I swipe and go to 2nd fragment, onCreateView of 3rd Fragments get called.
So, whatever I code in 2nd fragment execute in the first fragment view. I researched and came to know that it happen to keep the next fragment into memory for smooth animation. But I am wondering, where I would code in the fragment so that it will execute only once or how to restrict Fragment getItem() method to be called only once. What can be the solution for this?
According to the Fragment's documentation you can implement the onCreate() link:
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
This should fix your problem, because i guess you now only use the onCreateView which might be called more than once.
For more information you can check the Fragment's lifecycle or documentation
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().