I've recently made a refactoring of my code in order to use the Toolbar, TabLayout and TabLayout.Tab objects following this tutorial. The main structure (swipe and tab) works perfectly, as I'm able to move between them as I was expecting.
When moving between Fragments I use the setUserVisibleHint method to check if Fragment is visible and in case update the UI with fresh data coming from web services.
Recently I've got some error reports from customers complaining about fragments not being displayed with fresh data when clicking on Tab elements, while when using swipe everything works as expected.
Doing a little bit of research I've found out that:
ViewPager actually loads three fragments at once, calling their onResume(). It loads the visible fragment ant both of its neighbours (the one on the left and one on the right. It is needed for fluent sliding between fragments.
I've got confirmation of that by logging a line whenever onCreateView and onResume were called inside my fragments.
If I slide everything works as expected since Fragments are loaded in sequence, but if I jump between distant tabs (where distance is bigger than one) by clicking on them (for instance from tab in position 0 to tab in position 4) the latter Fragment isn't loaded by default by ViewPager and so (in this case Fragment in position 4) needs to be loaded once again.
I've fixed the issue adding this instruction as soon as the viewPagerAdapter is created:
viewPager.setOffscreenPageLimit(viewPagerAdapter.getCount());
Basically I'm forcing the adapter to load always all of the Fragments. I'm not convinced by this as I think it may cause performance issues.
I've read this SO question which seems interesting but I've also found out that the problem may be that I'm creating every time a new Fragment instance in the getItem() method of ViewPagerAdapter. Others suggest to use setRetainInstance(true) inside onCreateView of the Fragment.
I find the situation a little bit confusing and I don't know whether to proceed with the fix mentioned above or implement one of the suggestions found.
Below's the code for Tab creation and ViewPager inside TabBarActivity.
setApplicationContentView() called inside onCreate() method of TabBarActivity
private void setApplicationContentView() {
setContentView(R.layout.activity_tab_bar);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
tabLayout = (TabLayout) findViewById(R.id.tabs);
viewPager = (ViewPager) findViewById(R.id.pager);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
final ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(), TabBarActivity.this);
viewPager.setAdapter(viewPagerAdapter);
// Temp fix for fragments loading issue
viewPager.setOffscreenPageLimit(viewPagerAdapter.getCount());
tabLayout.setupWithViewPager(viewPager);
// Custom tab layout
setCustomTabLayout(0, R.raw.home);
setCustomTabLayout(1, R.raw.map_location);
setCustomTabLayout(2, R.raw.calendar);
setCustomTabLayout(3, R.raw.warning);
setCustomTabLayout(4, R.raw.tags);
}
ViewPagerAdapter
class ViewPagerAdapter extends FragmentPagerAdapter {
private TabBarActivity tabBarActivity;
public ViewPagerAdapter(FragmentManager manager, TabBarActivity tabBarActivity) {
super(manager);
this.tabBarActivity = tabBarActivity;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
homeFragment = HomeFragment.newInstance(tabBarActivity);
return homeFragment;
case 1:
mapFragment = MapFragment.newInstance(tabBarActivity);
return mapFragment;
case 2:
calendarFragment = CalendarFragment.newInstance(tabBarActivity);
return calendarFragment;
case 3:
warningsFragment = WarningsFragment.newInstance(tabBarActivity);
return warningsFragment;
case 4:
assignedFragment = AssignedFragment.newInstance(tabBarActivity);
return assignedFragment;
default:
return null;
}
}
@Override
public int getCount() {
return MPConstants.NUM_OF_FRAGMENTS;
}
@Override
public CharSequence getPageTitle(int position) {
Locale currentLocale = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_fragment_home).toUpperCase(currentLocale);
case 1:
return getString(R.string.title_fragment_map).toUpperCase(currentLocale);
case 2:
return getString(R.string.title_fragment_event_list).toUpperCase(currentLocale);
case 3:
return getString(R.string.title_fragment_warnings).toUpperCase(currentLocale);
case 4:
return getString(R.string.title_fragment_assigned).toUpperCase(currentLocale);
default:
return null;
}
}
}
Thanks in advance!