Loading Fragment UI on-demand

change fragment layout programmatically
fragment lifecycle
add fragment to activity
getuservisiblehint
start fragment from activity
dynamic fragment in android
fragment container view
android fragment vs activity

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.

Build a flexible UI, On a large screen, both fragments fit side by side, but on a handset device, only one fragment fits at a time so the fragments must replace each  A Fragment is a self-contained component with its own user interface (UI) and lifecycle that can be reused in different parts of an app's UI. (A Fragment can also be used without a UI, in order to retain values across configuration changes, but this lesson does not cover that usage.) A Fragment can be a static part of the UI of an Activity

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.

Fragments, A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and Configure on demand delivery · On demand delivery best practices new activity to show the summary, instead of loading a second fragment. In UI5 we can use fragments to split up a view in small pieces which you can reuse in other views. These fragments mainly contain parts of the UI and can be called from in a View or controller. When calling/opening a fragment you can also add a controller to this fragment, like for example in this blog:

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.

class sap.ui.core.Fragment, Example: Loading an XML fragment (default type) sap.ui.require(["sap/ui/core/​Fragment"], function(Fragment){ Fragment.load({ name: "my.useful. UI Real Estate: App Bar, Tabs, Pages and Fragments. The Android framework contains a number of UI components that allow a common feel to design simple UIs -- one is the ActionBar. These UI components integrate fragments which are associated with a view. Fragments let us build reusable and extensible UIs.

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.

Loading Fragment UI on-demand, 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. Sometimes your layout might require complex views that are rarely used. Whether they are item details, progress indicators, or undo messages, you can reduce memory usage and speed up rendering by loading the views only when they are needed.

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.

Dynamic Layouts using the Fragment Manager –, The UI drawn is shown below: In portrait mode the application will replace the existing titles fragment with the details fragment if the user taps on one of the names  Once you have defined your Fragment file.Now you have to call the Fragment into the Main view of your UI to dynamically load the codes in that Fragment file. The fragment file is called into the view using the following syntax <core:Fragment fragmentName=”<Your fragment file path>” type=”XML” />

Windows 8 Application Development with HTML5 For Dummies, The idea is to load the content “fragments” on demand and show them to the user​. I' m. ,swvvaamm: ,. I-I "MmIln vil- LD'W' UI*'IMU“Fn'*-I www all-a' was». Load On Demand works differently for igHierarchicalGrid, depending on whether you are using Ignite UI for jQuery or Ignite UI for MVC. The jQuery widget doesn’t have a specific property for Load On Demand, but can achieve this effect using the oData protocol, meaning the data must come from a remote server supporting that protocol.

Creating a Fragment, Note: FragmentActivity is a special activity provided in the Support Library to handle fragments on system versions older than API level 11. If the lowest system​  Step 2: Load the fragment. Our next step will be to load the fragment definition for each control. Luckily fragments can be instantiated programmatically. By doing so we will get an array of controls defined by this fragment. We need to load our fragment controls in the init method of our base

Building a Flexible UI, On a large screen, both fragment fit side by side, but on a handset device, only one fragment fits at a time so the fragments must replace each other as the user  Hello, one observation is that the function to open Dialog fragment is named as onOpenDialog inside HelloPanel Controller, while inside the HelloPanel view you are calling an undefined function onShowDialog.

Comments
  • Have you looked into FragmentStatePagerAdapter.
  • @TaseerAhmad yes I have. The ViewPager adapter is already FragmentStatePagerAdapter. It doesn't really have any impact that I can see.
  • @ᴛʜᴇᴘᴀᴛᴇʟ have you heard about setOffscreenPageLimit in the ViewPager?
  • Could you add minimum working code that can help us to try out?
  • there’s an API called ViewStub, which can be used to lazy inflate views.
  • 1) You can’t call those fragments redundent 2) What you get is ‘PagerView’ that’s controlled with both ‘TabLayout’ and ‘BottomNavigationView’. What OP wants is to have 2 Fragments to be controlled by ‘BottomNavigationView’ and the rest to be handled by ‘TabLayout’ inside those Fragments. Simply what you did is to alter all the design to have only ‘ViewPager’. Please, look at the example video OP referecing what actually they want to accomplish.
  • @Farid 1) I think I can. Can you please provide any reference that strengthen your statement? 2) I think OP can achieve the same behavior that is shown in the video with my solution. Also, my solution addresses his main concern i.e. too many fragment loading issue when the app is opened.
  • @Farid I would recommend you to focus on your own answer to give an optimum solution as I think it doesn't address the 'too many fragment loading issue' because FragmentTransaction.hide() only hides the fragment whose view is already added to the container. You can see more details here, developer.android.com/reference/android/support/v4/app/…