2

I am adding a BottomNavigationView with three fragments to my app. Everything works correctly, except one thing.

In the first fragment, there is an EditText view, in the second a ListView and in the third one some texts and images loaded from a JSON hosted in the server.

This is my code:

bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
          @Override
          public boolean onNavigationItemSelected(@NonNull MenuItem item) {
              int id = item.getItemId();
              switch (id) {
                  case R.id.menu_dash:
                      fragment = new frg_dash();
                      break;
                  case R.id.menu_list:
                      fragment = new frg_list();
                      break;
                  case R.id.menu_info:
                      fragment = new frg_info();        
                      break;
              }
              final FragmentTransaction transaction = fragmentManager.beginTransaction();
              transaction.replace(R.id.contenedor_principal, fragment).commit();

              return true;
          }
      });

The problem is that every time I go from one fragment to another using the bottomNavigationView buttons, the Fragment starts all its execution again.

The result I'm looking for is that if the user in the second fragment is, for example, in the middle of the ListView, it goes to the third fragment and returns again, the ListView continues where it was. Or if you press the button of the third Fragment in the bottomNavigationView, do not load the data again from the server.

I guess the problem is that when you click on a bottomNavigationView button, the fragment is created again:

 ... switch (id) {
      case R.id.menu_dash:
      fragment = new frg_dash();
      break; ...

But it's just a guess. I suppose it can be controlled with the onCreate, onActivityCreated and onCreateView methods, but again, they are just my assumptions.

I've tried it with the hide () and show () parameters of the fragments, but without success ... or I'm not applying it well

I greatly appreciate the help in advance.

EDIT

This is my example currently with all the parts related to the answer:

public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {

    Log.v("2134", "Dentro");

    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    String tag = fragment.getClass().getName();
    Log.v("2134", "tag:" + tag);
    Fragment parentFragment;
    if (findInStack && fm.findFragmentByTag(tag) != null) {
        parentFragment = fm.findFragmentByTag(tag);
    } else {
        parentFragment = fragment;
    }
    // if user passes the @bundle in not null, then can be added to the fragment
    if (bundle != null) {
        parentFragment.setArguments(bundle);
    } else {
        parentFragment.setArguments(null);
    }
    // this is for the very first fragment not to be added into the back stack.
    if (popBackStack) {
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    } else {
        ft.addToBackStack(parentFragment.getClass().getName() + "");
    }
    ft.replace(R.id.contenedor_principal, parentFragment, tag);
    ft.commit();
    fm.executePendingTransactions();
}

...

bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case R.id.menu_panel:
                fragment = new frg_panel();
                break;
            case R.id.menu_promos:
                fragment = new frg_promociones();
                break;
            case R.id.menu_catalogo:
                fragment = new frg_catalogo();
                break;
        }

        replaceFragment(fragment, null, true, true);

        return true;
    }
});
Sergio76
  • 3,835
  • 16
  • 61
  • 88
  • How did you solve this recreating issue? Can you please shed some light? I have tried many solutions. But nothing working. – Mac_Play Jan 18 '21 at 09:04

1 Answers1

5

Use this code to open your fragment. Your fragment will not create every time. It will get same fragment from stack if exist.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

use it like

Update :

If your fragment is home or dashboard fragment then

Fragment f = new YourFragment();
replaceFragment(f, null, true, true); 

Otherwise

Fragment f = new YourFragment();
replaceFragment(f, null, false, true); 

Important This code is not replacement of saving all states or variables in fragment. This code will useful because it will not create fragment instance again.

For saving all states and variables in fragment for future use see this answer

Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
  • I've tried the code, but it continues loading the full fragment again :( – Sergio76 Apr 12 '18 at 19:17
  • See you dont set popBackStack true. – Khemraj Sharma Apr 13 '18 at 04:59
  • I do not understand what may be failing, the method that I am using is copied from the one that you have put me in the answer, and to call it use: fragment = new frg_list(); replaceFragment(fragment, null, true, true); The popBackStack parameter is true, as you indicate. I'm somewhat desperate :) What am I doing wrong? – Sergio76 Apr 13 '18 at 09:56
  • What is issue? that if you replace fragment again and replace back to first fragment again, does your fragment reload? I dont think it should be, i use this code in navigation drawer, where user can visit any fragment again and again, by this method i load previously initiated fragment. – Khemraj Sharma Apr 13 '18 at 10:05
  • Well, I do not know what the problem may be, every time I click on a fragment again, it calls the methods onCreate, onCreateView and onActivityCreate, therefore, the fragment always starts again at each click in the bottomNavigationView – Sergio76 Apr 13 '18 at 10:13
  • Its my mistake, when i wrote sample calling method, i forgot to set popstack boolean false, if it is true it will clear all stack so you will never get old fragment. – Khemraj Sharma Apr 13 '18 at 10:13
  • I updated my answer ,Let me know this solves your issue. – Khemraj Sharma Apr 13 '18 at 10:13
  • 413/5000 First of all, thank you very much for your kind help. Unfortunately the fragment is still not recovered from the pile. There is no fragment "home", the BottomNavigationView view is in the activity that contains the fragment container. With the change to false of the popBackStack parameter, entering the fragments does not go through the onCreate method again, but launches onActivityCreate and onCreateView again. – Sergio76 Apr 13 '18 at 10:33
  • The code where you fetching data, you can check if your old variable is not empty or null, then fetch otherwise just set old data. – Khemraj Sharma Apr 13 '18 at 10:37
  • Sorry, but I do not understand very well what variable you are referring to. However, based on your indications, if I have observed that the value of the bundle is always null, I do not think that is correct, or yes? – Sergio76 Apr 13 '18 at 10:46
  • Bundle is for passing any data to fragment if you want. Variable in sense of like if you have listview then you have an arraylist in your fragment. You can check if that arraylist is empty. if empty then only fetch data. I also managed it like this. – Khemraj Sharma Apr 13 '18 at 10:52
  • t's not exactly what I was looking for, but it can serve! Thank you so much for all your help! – Sergio76 Apr 13 '18 at 11:02
  • I am happy to help you, you need to save states onSaveInstanceState if you want load all previous state, see link in updated answer. – Khemraj Sharma Apr 13 '18 at 11:13