I want to know more about ViewPager behavior. I have a FragmenPagerAdapter :
public class DatePagerAdapter extends FragmentPagerAdapter {
public DatePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, 1);
int offset = position - 100;
calendar.add(Calendar.MONTH, offset);
// Toast.makeText(TestActivity.this,(calendar.get(Calendar.MONTH)+1)
Log.v("CALENDAR", "" + position);
TestFragmentDate date = TestFragmentDate.newInstance(TestActivity.this, calendar);
return date;
}
#Override
public int getCount() {
return 200;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
}
And the code in my Activity :
adapter = new DatePagerAdapter(getSupportFragmentManager());
MinFragmentPagerAdapter wrapperMin = new MinFragmentPagerAdapter(getSupportFragmentManager());
wrapperMin.setAdapter(adapter);
PagerAdapter wrapper = new InfinitePagerAdapter(wrapperMin);
viewPager = (ViewPager) this.findViewById(R.id.pager);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(100);
viewPager.addOnPageChangeListener(this);
According to my senior, ViewPager always draw 3 fragments, and keep reuse them. For example, at first I have view at position :
99, 100, 101
If I roll right, it will destroy 99 and create 102, and so on.
But, when I debug at the function getItem, at first it did run into this function, but when I roll right for 5 or 6 page, and then roll back, it didn't run into getItem, at the position which is supposed to be destroyed.
So would anyone please explain for me about ViewPager's behavior ? Thank you.
Google's guide says:
FragmentPagerAdapter
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.
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.
And about FragmentStatePagerAdapter:
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the
saved state of that fragment. This allows the pager to hold on to much
less memory associated with each visited page as compared to
FragmentPagerAdapter at the cost of potentially more overhead when
switching between pages.
Conclusion : Use FragmentStatePageAdapter in your case, that is you don't want Fragment attached to your ViewPager to be destroyed.
You can also use viewpager.setOffscreenPageLimit(<no of fragments>); to limit How many pages will be kept offscreen in an idle state.
As said before:
You can also use viewpager.setOffscreenPageLimit(<no of fragments>);
to limit How many pages will be kept offscreen in an idle state.
From the official documentation:
setOffsetPageLimit(): Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
It means setOffScreenPageLimit(1) will keep 1 page to the left and/or 1 page to the right depending on what position of the viewPager you are.
Related
Problem:
I am currently running into a problem where my app is trying to load too many fragments when it opens for the first time.
I have BottomNavigationView with ViewPager that loads 4 fragments - each one of the Fragment contains TabLayout with ViewPager to load at least 2 more fragments.
As you can imagine, that is a lot of UI rendering (10+ fragments) - especially when some of these fragments contain heavy components such as calendar, bar graphs, etc.
Currently proposed solution:
Control the UI loading when the fragment is required - so until the user goes to that fragment for the first time, there is no reason to load it.
It seems like it's definitely possible as many apps, including the Play Store, are doing it. Please see the example here
In the video example above - the UI component(s) are being loaded AFTER the navigation to the tab is completed. It even has an embedded loading symbol.
1) I am trying to figure out how to do exactly that - at what point would I know that this fragment UI need to be created vs it already is created?
2) Also, what is the fragment lifecycle callback where I would start the UI create process? onResume() means UI is visible to the user so loading the UI there will be laggy and delayed.
Hope this is clear enough.
EDIT:
I'm already using the FragmentStatePagerAdapter as ViewPager adapter. I noticed that the super(fm) method in the constructor is deprecated now:
ViewPagerAdapter(FragmentManager fm) {
super(fm); // this is deprecated
}
So I changed that to:
ViewPagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT: Indicates that only the current fragment will be in the Lifecycle.State.RESUMED state. All other Fragments are capped at Lifecycle.State.STARTED.
This seems useful as the onResume() of the Fragment will only be called when the Fragment is visible to the user. Can I use this indication somehow to load the UI then?
The reason your app loads multiple Fragments at the startup is most probably, you're initializing them all at once. Instead, you can initialize them when you need them. Then use show\ hide to attach\ detach from window without re-inflating whole layout.
Simple explanation: You'll create your Fragment once user clicks on BottomNavigationView's item. On clicked item, you'll check if Fragment is not created and not added, then create it and add. If it's already created then use show() method to show already available Fragment and use hide() to hide all other fragments of BottomNavigationView.
As per your case show()/hide is better than add()/replace because as you said you don't want to re-inflate the Fragment when you want show them
public class MainActivity extends AppCompatActivity{
FragmentOne frg1;
FragmentTwo frg2;
#Override
public boolean onNavigationItemSelected(MenuItem item){
switch(item.getId()){
case R.id.fragment_one:
if (frg2 != null && frg2.isAdded(){
fragmentManager.beginTransaction().hide(frg2).commit();
}
if(frg1 != null && !frg1.isAdded){
frg1 = new FragmenOne();
fragmentManager.beginTransaction().add(R.id.container, frg1).commit();
}else if (frg1 != null && frg1.isAdded) {
fragmentManager.beginTransaction().show(frg1).commit();
}
return true;
case R.id.fragment_two:
// Reverse of what you did for FragmentOne
return true;
}
}
}
And for your ViewPager as you can see from the example you're referring to; PlayStore is using setOffscreenPageLimit. This will let you choose how many Views should be kept alive, otherwise will be destroyed and created from start passing through all lifecycle events of the Fragment (in case view is Fragment). In PlayStore app's case that's probably 4-5 that why it started loading again when you re-selected "editor's choice" tab. If you do the following only selected and neighboring (one in the right) Fragments will be alive other Fragments outside screen will be destroyed.
public class FragmentOne extends Fragment{
ViewPager viewPager;
#Override
public void onCreateView(){
viewPager = .... // Initialize
viewpAger.setOffscreenPageLimit(1); // This will keep only 2 Fragments "alive"
}
}
Answer to both questions
If you use show/hide you won't need to know when to inflate your view. It will be handled automatically and won't be laggy since it's just attaching/detaching views not inflating.
It depends upon how you initialize your fragment in your activity. May be you are initializing all your fragment in onCreate method of your activity instead of that you can initialize it when BottomNavigation item is selected like below :
Fragment one,two,three,four;
#Override
public boolean onNavigationItemSelected(MenuItem item){
Fragment fragment;
switch(item.getId()){
case R.id.menu_one:{
if(one==null)
one = Fragment()
fragment = one;
break;
}
case R.id.menu_two:{
if(two==null)
two = Fragment()
fragment = two;
break;
}
}
getFragmentManager().beginTransaction().replace(fragment).commit();
}
To decide how many page is load in you view pager at one time you can use :
setOffscreenPageLimit.
viewPager.setOffscreenPageLimit(number)
To get the resume and pause functionality on fragments you can take an example from this link.
Please try this.
i was worked with the same kind of the Application, There were multiple tabs and also Tabs have multiple inner tabs.
i was used the concept of ViewPager method, In which there is one method of onPageSelected() for that method we were getting the page position.
By the Use of this position we are checking the current Fragment and called their custom method that we created inside that fragment like onPageSelected() defined inside that fragment.
With this custom method onPageSelected() inside the Fragment we checked that weather the list are available or not if list have data then we are not making the call of Api otherwise we are calling the Api and loading that list.
I think you have same kind of requirement to follow if your Tabs have inner Tab or viewpager you can follow same concept inside of that so if your current fragment of viewpager method onpageSelected called at that time your viewpager fragment initialized.
you have to call just initialization like data binding or view initialization need to be called in onCreate() method and other list attachment and api call to be managed by the custom method onPageSelected that will be called based on ViewPager onPageSelected.
let me Know if you need any help for same.
You can try to have Fragments with FrameLayouts only in ViewPager. The actual Fragments could be added to FrameLayout in onResume() (after checking if this Fragment isn't already attached). It should work if BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT works as expected.
I would recommend you use BottomNavigationView.setOnNavigationItemSelectedListener to toggle between the fragment UI whenever it is needed.
navigationView.setOnNavigationItemSelectedListener(item -> {
switch(item.getItemId()) {
case R.id.item1:
// you can replace the code findFragmentById() with findFragmentByTag("dashboard");
// if you only have one framelayout to hold the fragment
fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = new ExampleFragment();
// if the fragment is identified by tag, add another
// argument to this method:
// replace(R.id.fragment_container, fragment, "dashboard")
getSupportFragmentManager().begintransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
break;
}
}
The idea is simple, when the user swipes or selects a different tab, the fragment that was visible is replaced by the new fragment.
Just load fragments one by one. Create the main fragment layout with many placeholders and stubs and then just load them in the order you like.
Use FragmentTransaction.replace() from the main fragment after it loads.
Have you tried the setUserVisibleHint() method of a fragment
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if(isVisibleToUser){
// Do you stuff here
}
}
This will only get called when a fragment is visible to the user
How about you maintain just one ViewPager? Sounds crazy? In that case, you just change the dataset of PagerAdapter when you switch between the bottom tabs. Let's see how you can accomplish this,
As you mentioned, you have 4 fragments, which are assigned to each individual tabs of the bottom navigation view. Each performs some redundant work i.e. holding a viewPager with tab layout and setting the same kind of adapters. So, if we can combine these 4 redundant tasks into one then we will be able to get rid of 4 fragments. And as there will be just one viewPager with one single adapter then we will be able to reduce the fragment loading count from ~10 to 2 if we set offScreenPageLimit to 1. Let's see some example,
activity.xml should look like
<LinearLayout>
<TabLayout />
<ViewPager />
<BottomNavigationView />
</LinearLayout>
It's optional but I would recommend to create a base PagerFragment abstract class with abstract method getTabTitle()
public abstract class PagerFragment extends Fragment {
public abstract String getTabTitle();
}
Now it's time to make our PagerAdapter class
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public Map<Integer, List<PagerFragment>> map = ...; // If you are concerned about memory then I could recommend to store DataObject instead of PagerFragment and instantiate fragment on demand using that data.
public int currentTabId = R.id.first_bottom_tab_id;
private List<PagerFragment> getCurrentFragments() {
return map.get(currentTabId);
}
public void setCurrentTabId(int tabId) {
this.currentTabId = tabId;
}
public SectionsPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return getCurrentFragments().get(position);
}
#Override
public int getCount() {
return getCurrentFragments().size();
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
return getCurrentFragments().get(position).getTabTitle();
}
}
And finally, in Activity
SectionsPagerAdapter pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
viewPager.setOffscreenPageLimit(1);
viewPagerTab.setViewPager(viewPager);
bottomNavigationView.setOnNavigationItemSelectedListener(menuItem -> {
pagerAdapter.setCurrentTabId(menuItem.getItemId())
pagerAdapter.notifyDataSetChanged();
viewPagerTab.setViewPager(viewPager);
}
This is the basic idea. You can mix some of your own ideas with it to make a wonderful result. Let me know if it is useful?
UPDATE
Answer to your questions,
I think with my solution you can achieve exactly the same behavior of the video as I already did it in a project. In my solution, if you set offset page limit to 1 then only adjacent fragment's is created in advance. So, fragment creation will be handled by adapter and viewpager you don't need to worry about it.
In my above solution, you should create UI in onCreateView().
A ViewPager preloads fragments.
But with a pageWidth=0.5 (2 fragments per screen), I think it loads too much views before the current position...
I have pageLimit=1 and pageWidth=0.5.
It should be ok to preload LEFT1 and LEFT2, but why LEFT3 ?
LEFT3 is not on the next left screen...
I tried to understand ViewPager.java from support v4:
https://android.googlesource.com/platform/frameworks/support/+/support-library-27.1.1/core-ui/src/main/java/android/support/v4/view/ViewPager.java#1155
line 1155, it says
// Fill 3x the available width or up to the number of offscreen
// pages requested to either side, whichever is larger.
But for multiple fragments per screen, it loads more before than current and after the current position.
With the Google sample project https://github.com/googlesamples/android-SlidingTabsBasic/
adding
protected void onCreate(Bundle savedInstanceState) {
[...]
mViewPager.setCurrentItem(25);
#Override
public float getPageWidth(int position) {
// 2 fragments per screen
return .5f;
}
#Override
public int getCount() {
// 50 fragments
return 50;
}
public Fragment getItem(int position) {
[...]
Log.d("debugpageviewer", "position="+position);
}
And it loads 7 fragments (3 before current position). The smaller the pageWidth, the more fragments are loaded before.
So, is this a bug or did I miss something ?
Try calling setOffscreenPageLimit() on your ViewPager in your onCreate method.
For example mViewPager.setOffscreenPageLimit(2) will make sure that no more than 3 fragments are always kept around: the current one and the two adjacent ones.
I'm using ViewPager and FragmentPagerAdapter within a detail-view. This means I have a list of items and one screen shows the details of a single item. But the user can swipe left and right to navigate through all items. This follows the Google guideline for swiping views.
But I wonder about one thing. Within a ListView the views for each row get re-used. Once a row scrolls out of the screen it is re-used as the convertView parameter of the getView method of the adapter that is bound to the ListView. But this re-usage behavior does not seem to be implemented for swiping views. This example illustrates this:
class DemoAdapter extends ArrayAdapter<DemoItem> {
public DemoAdapter(Context context, List<DemoItem> objects) {
super(context, 0, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// create a new view, otherwise re-use the existing convertView
LayoutInflater i = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = i.inflate(R.layout.list_item_demo, parent, false);
}
// get current item
DemoItem item = getItem(position);
if (item == null)
return convertView;
// update view with the item
TextView textTitle = (TextView)convertView.findViewById(R.id.demo_title);
if (textTitle != null)
textTitle.setText(item.getTitle());
return convertView;
}
}
But here's the problem: Both, the FragmentPagerAdapter and the FragmentStatePagerAdapter are creating the fragments (each screen is a fragment) in their getItem method. But they don't get old fragments as an input parameter. The only difference is, that the FragmentStatePagerAdapter destroys unused fragments.
public class DemoItemsPagerAdapter extends FragmentPagerAdapter {
private final Context context;
public DemoItemsPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
// ToDo: get cursor or array of available items that can be swiped through
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new DemoItemFragment();
// ToDo: initialize fragment by correct item
// ToDo: avoid creating too many fragments - try reusing them (but how?)
return fragment;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
// the container is not the fragment, but the ViewPager itself
return super.instantiateItem(container, position);
}
#Override
public CharSequence getPageTitle(int position) {
// ToDo: return name for current entry
return null;
}
#Override
public int getCount() {
// ToDo: get count from cursor/array of available items
return 2;
}
}
So, how can I reuse the fragments? Actually getItems should only be called twice because there is only one fragment visible at a time and a second one once the transition starts while the user is swiping.
UPDATE: Because of confusion, I created this drawing. It shows the behavior of the adapters. The default one keeps all fragments in memory unless the device runs out of memory. One the app is in background or killed and then restored each fragment will be restored from its SavedInstanceState. The second implementation keeps only some fragments in memory but if you swipe left/right the destroyed ones will be completely created again from scratch. The third implementation is what I'm seeking. You have only three fragments which are then reused when swiping left or right. So fragment A can be position 1, 2, 3, 4, 5, 6 and 7.
Firstly to answer your question.
So, how can I reuse the fragments?
You can maintain an array of fragments in a SparseArray (It is more memory efficient than a HashMap when you need to map objects to integers).
private SparseArray<BaseFragment> fragments;
So your code can be something like this in the getItem(int i).
#Override
public Fragment getItem(int position) {
// Create a new fragment only when the fragment is not present in the fragments sparse
// array
BaseFragment fragment = fragments.get(position);
if (fragment == null) {
switch (position) {
case 0: {
fragment = new Fragment1();
fragments.put(0, fragment);
break;
}
case 1: {
fragment = new Fragment2();
fragments.put(1, fragment);
break;
}
.
.
.
default:
fragment = null;
}
}
return fragment;
}
Here I use a BaseFragment which is extended by almost all the fragments that I want to use.
And AFAIK, getItem() is called based upon the offScreenPageLimit. The default is 1. So based upon this number, the fragments that will be kept in memory will be
1 + 2*offScreenPageLimit // Current Page + 2 * left/right items
or
1 + offScreenPageLimit // if its the first or last page.
UPDATE 1
You dont have to worry about handling the removal of fragments from the memory. The FragmentStatePagerAdapter automatically handles that for you as mentioned in their docs.
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the
saved state of that fragment. This allows the pager to hold on to much
less memory associated with each visited page as compared to
FragmentPagerAdapter at the cost of potentially more overhead when
switching between pages.
You use FragmentPagerAdapter when you've less pages to swipe, generally when using tabs or when the fragments are static. The doc says,
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.
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.
UPDATE 2
Your definition above for case 2 is wrong.
The second implementation keeps only some fragments in memory but if
you swipe left/right the destroyed ones will be completely created
again from scratch.
It wont be created from scratch, it will save the state of the previously created fragment and use the same state while creating the new one. You can look at the source code here to better understand how it works.
As to your 3rd implementation, I'd suggest overriding the default behavior of the adapter and then manually removing/adding view from the adapter based upon the current position.
Since in Android L the the Action bar navigation modes are deprecated I'm searching an other way to have the tab and I found that is possible to use the PagerTabStrip (android.support.v4.view.PagerTabStrip), so I created a FragmentPageAdapter in this way:
public class TitleAdapter extends FragmentPagerAdapter {
private final String titles[] = new String[] { "Home", "Events", "People", "Books"};
private final Fragment frags[] = new Fragment[titles.length];
Context context;
public TitleAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
Log.v("TitleAdapter - getPageTitle=", titles[position]);
return titles[position];
}
#Override
public Fragment getItem(int position) {
Log.v("TitleAdapter - getItem=", String.valueOf(position));
//return frags[position];
switch (position) {
case 0:
return Home.newInstance(0, "Home");
case 1:
return Events.newInstance(1, "Events");
case 2:
return People.newInstance(2, "People");
case 4:
return Books.newInstance(3, "Books");
}
return null;
}
#Override
public int getCount() {
return frags.length;
}
}
The strange way that i see in LogCat is that the method getItem() is called 4 times when the mainActivity starts so I've to wait a lots because in each Tab there is a quite long list and this list is populated via HTTP request calling a web service.
I wish load only a fragment each times and not all. When i used actionbar.Tablistener it was possible but now the method is deprecated so is there a way to do that?
I set the adapter and the viewPager in the onCreate method in this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.prova_page_tab_stripes);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
titleAdapter = new TitleAdapter(getSupportFragmentManager());
mViewPager.setAdapter(titleAdapter);
mViewPager.setOffscreenPageLimit(1);
}
The number of pages initialized depends on setOffscreenPageLimit function of ViewPager. As per android doc in http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int),
public void setOffscreenPageLimit (int limit)
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
This is offered as an optimization. If you know in advance the number of pages you will need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting can have benefits in perceived smoothness of paging animations and interaction. If you have a small number of pages (3-4) that you can keep active all at once, less time will be spent in layout for newly created view subtrees as the user pages back and forth.
You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.
Parameters
limit How many pages will be kept offscreen in an idle state.
Set it to 1 or keep it to default if you wan to limit pages to be retained.
But if you want to load the data only in one page at a time, you can determine when fragment becomes visible and then load the data (insted of loading in onCreateView.)
Refer this question: How to determine when Fragment becomes visible in ViewPager
Using SDK 19, min 13, and support.v4.app Fragments.
I have searched SO and found similiar threads which should help, but everything I have tried has not addressed my issue yet. Furthermore, many people seem to have the issue of the ViewPager restarting when they don't want it to, whereas my problem seems to be the opposite.
These posts do not seem to have helped me yet:
PagerAdapter start position
ViewPager PagerAdapter not updating the View
How to force ViewPager to re-instantiate its items
Here is my setup:
A (support.v4) Fragment in memory, which contains a ViewPager, inflated from a layout file, thus not added programmatically. The Fragment itself is not recreated, but in memory for the life of the app; it is being attached/detached to the root FragmentActivity's FragmentManager.
When the user visits this Fragment sometime during the app's lifetime, it is attached and the onCreateView is called, where I findViewById the ViewPager and assign a new FragmentStatePagerAdapter to it.
Here is my problem:
The first time the user visits this pager, everything works fine. The next time they visit it, I expect it to "start over" from page 0, meaning that I expect to not be able to scroll left immediately, but must start scrolling right. I also expect the getItem() of the FragmentStatePagerAdapter to start back at position 0. I basically want it to function the same as the first time the user visited it. However, this is not the case.
I have tried several things, but it always seems to start at the previous index of page I left it at. So if the first time I scrolled 5 pages over, and then left the Fragment and returned later, the ViewPager starts at that same page index, meaning I can scroll 5 pages left. I don't want this.
It may have something to do with the ViewPager or FragmentStatePagerAdapter storing pages internally, which are not being released. But I thought the commented out code I tried would have done that. There might be another way that I have not tried. Perhaps it is related to not recreating my Fragment, but doing so at this point in my architecture will not be great. I was hoping I could restart the ViewPager without doing this.
Here is my code:
My Fragment's onCreateView code. Everything commented out I have tried without success.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_playscreen, container, false);
//while (getChildFragmentManager().popBackStackImmediate()) {}
mAdapter = new AdapterPlayPages(this, getChildFragmentManager());
mPager = (ViewPlayPager) view.findViewById(R.id.pagerPlayScreen);
//mPager.setAdapter(null);
mPager.storeAdapter(mAdapter);
//mPager.setCurrentItem(0);
//mPager.removeAllViews();
return view;
}
My ViewPager, which has been overridden like so to get around a completely unrelated bug, as suggested here: https://stackoverflow.com/a/19900206/1002098. This does not effect the problem; I removed these changes so that I could call setAdapter(null) as suggested, but it did not address my question.
public class ViewPlayPager extends ViewPager
{
PagerAdapter mPagerAdapter;
public ViewPlayPager(Context context)
{
super(context);
}
public ViewPlayPager(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
if (mPagerAdapter != null)
{
//super.setAdapter(null); // did not help
super.setAdapter(mPagerAdapter);
}
}
#Override
public void setAdapter(PagerAdapter adapter)
{
// do nothing
}
public void storeAdapter(PagerAdapter adapter)
{
mPagerAdapter = adapter;
}
}
My FragmentStatePagerAdapter. I've removed some unrelated logic and members.
public class AdapterPlayPages extends FragmentStatePagerAdapter
{
private FragmentPlayScreen mParent;
public AdapterPlayPages(FragmentPlayScreen parent, FragmentManager fragmentManager)
{
super(fragmentManager);
mParent = parent;
}
#Override
public int getCount()
{
// some logic from parent to determine true count
// the count shouldn't effect the position to start at
return Integer.MAX_VALUE;
}
#Override
public Fragment getItem(int position)
{
// logic to determine type of page to display,
// which is not changing based on position at the moment
return new FragmentPlayPage();
}
// this did not seem to help me
//#Override
//public int getItemPosition(Object object)
//{
// //return super.getItemPosition(object);
// return POSITION_NONE;
//}
}
I had to change my architecture to recreate the Fragments - add/remove them from FragmentManager, as opposed to attach/detach them while kept in memory.
It is not clear to me why the Fragment would have to be recreated for the ViewPager to reset. Android FragmentManager and FragmentTransaction supports keeping Fragments in memory and simply attaching/detaching them, or showing/hiding them, so it should be possible to simply reset the ViewPager from the in-memory Fragment's onCreateView event, but again, none of the commented out calls above seemed to work.
If anyone else has a solution, I'd still consider it. It will help me understand this issue.