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 View
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With