Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save state when navigating between fragments

I'm working on an app and I have a menu with a NavigationDrawer to navigate between fragments. In one of the fragments I make a call to the backend and then save the results in a list. When I navigate to another fragment and back, the results are gone, but I'd like to save the contents of the list temporarily. I wanted to use onSaveInstanceState(), but that method doesn't seem to get called ever. I also looked if the data is still in the fields when I return to the fragment, but that also wasn't the case. I think I'm doing something wrong with the FragmentManager, but I'm not sure about it.

This is the method used for the transactions for the Fragments:

private void openFragment(Class fragmentClass) {
    Fragment fragment;
    try {
        fragment = (Fragment) fragmentClass.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return;
    }
    contentFrame.removeAllViews();
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.contentFrame,fragment).commit();
}

I use a switch case to determine the Fragment's class and send that to this method.

I could probably figure out a hacky-snappy way to fix this, but I'd like to fix this without too much hacky-snappy code.

I hope someone has an idea on how to fix this. Thanks in advance.

EDIT:

Here is my fragment class:

public class LGSFragment extends Fragment {

    @BindView(R.id.rvLGS)
    RecyclerView rvLGS;

    private List<LGS> lgsList;
    private LGSAdapter adapter;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //I debugged here and all fields were null at this point
        View view = inflater.inflate(R.layout.fragment_lgs,container,false);
        ButterKnife.bind(this, view);
        lgsList = new ArrayList<>();
        LinearLayoutManager manager = new LinearLayoutManager(getContext());
        rvLGS.setLayoutManager(manager);
        adapter = new LGSAdapter(lgsList);
        rvLGS.setAdapter(adapter);
        getDatabaseLGSs();
        return view;
    }

    /**
     * Method to load in the LGSs from the database
     */
    private void getDatabaseLGSs() {
        String collection = getString(R.string.db_lgs);
        FireStoreUtils.getAllDocumentsConverted(collection, LGS.class, new OperationCompletedListener() {
            @Override
            public void onOperationComplete(Result result, Object... data) {
                if (result == Result.SUCCESS) {
                    lgsList.clear();
                    List<LGS> newLGSs = (List<LGS>) data[0];
                    List<String> ids = (List<String>) data[1];
                    int i = 0;
                    for (LGS lgs : newLGSs) {
                        lgs.setId(ids.get(i));
                        lgsList.add(lgs);
                        i++;
                    }
                    adapter.notifyDataSetChanged();
                }
            }
        });
    }


    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
    }
}
like image 870
Dennis van Opstal Avatar asked Sep 12 '25 16:09

Dennis van Opstal


2 Answers

onSaveInstanceState is not called because there is no reason to, when you navigate between fragments, the older fragment doesn't get destroyed till the OS need the space they use (low Memory).

First of all create a back stack to keep fragments or just call addtoBackStack at the end of fragmentTransaction and then move the list initiation and data request to onCreate so it only called when the fragment is created:

    lgsList = new ArrayList<>();
    getDatabaseLGSs();

and after that every time you get back to fragment the view is recreated with available data.

Update: Instead of keeping an reference on your own, you can add the fragment to the backstack and then retrieve it using corresponding tag. This let's fragmentManager manages the caching by itself. And the second time you access a fragment, it doesn't gets recreated:

@Override
public void onNavigationDrawerItemSelected(@NonNull MenuItem item) {
    if (item.isChecked())
        return;

    item.setChecked(true);
    setTitle(item.getTitle());

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    Fragment currentlyShown = fragmentManager.findFragmentByTag(currentlyShownTag);

    Fragment dest;
    switch (item.getItemId()){
        case R.id.nav_lgs:
            dest = fragmentManager.findFragmentByTag(LGSFragment.class.getName());
            if (dest == null) {
                Log.d("TRANSACTION", "instanciating new fragment");
                dest = new LGSFragment();
                currentlyShownTag = LGSFragment.class.getName();
                transaction.add(R.id.contentFrame, dest, LGSFragment.class.getName());
            }
            break;
            ...


    }

    if(currentlyShown != null)
        transaction.hide(currentlyShown);

    transaction.show(dest);
    transaction.commit();
    drawerLayout.closeDrawers();
    return true;
}
like image 54
Keivan Esbati Avatar answered Sep 14 '25 06:09

Keivan Esbati


EDIT: Although this solution works fine, this solution uses some bad practices, I recommend using the accepted solution instead.

I've solved the problem with the help of Keivan Esbati and denvercoder9 (Thanks for that!)

Since I only have 4 fragments I keep an instance of each of them in the MainActivity, I also have a variable to track the current Fragment. Everytime I open a fragment, I hide the current fragment using the FragmentManager and calling .hide() in the transaction. Then, if the Fragment is a new Fragment I call .add() in the transaction, otherwise I call .show in the transaction.

The code for the onNavigationItemSelected() method (which triggers when a user selects an item in the menu):

public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    if (!item.isChecked()) {
        item.setChecked(true);
        setTitle(item.getTitle());
        switch (item.getItemId()) {
            case R.id.nav_lgs: {
                if (lgsFragment == null) {
                    lgsFragment = new LGSFragment();
                    openFragment(lgsFragment, FragmentTag.LGS.toString());
                } else {
                    openFragment(lgsFragment, "");
                }
                currentFragmentTag = FragmentTag.LGS;
                break;
            }
            case R.id.nav_users: {
                if (userFragment == null) {
                    userFragment = new UserFragment();
                    openFragment(userFragment, FragmentTag.USERS.toString());
                } else {
                    openFragment(userFragment, "");
                }
                currentFragmentTag = FragmentTag.USERS;
                break;
            }
            case R.id.nav_profile: {
                if (profileFragment == null) {
                    profileFragment = new ProfileFragment();
                    openFragment(profileFragment, FragmentTag.PROFILE.toString());
                } else {
                    openFragment(profileFragment, "");
                }
                currentFragmentTag = FragmentTag.PROFILE;
                break;
            }
            case R.id.nav_my_lgs: {
                if (myLGSFragment == null) {
                    myLGSFragment = new MyLGSFragment();
                    openFragment(myLGSFragment, FragmentTag.MY_LGS.toString());
                } else {
                    openFragment(myLGSFragment, "");
                }
                currentFragmentTag = FragmentTag.MY_LGS;
                break;
            }
            default: {
                if (lgsFragment == null) {
                    lgsFragment = new LGSFragment();
                    openFragment(lgsFragment, FragmentTag.LGS.toString());
                } else {
                    openFragment(lgsFragment, "");
                }
                currentFragmentTag = FragmentTag.LGS;
                break;
            }
        }
    }
    drawerLayout.closeDrawers();
    return true;
}

The openFragment() method used above:

private void openFragment(Fragment fragment, String tag) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (currentFragmentTag != FragmentTag.NO_FRAGMENT) {
        fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag(currentFragmentTag.toString())).commit();
    }
    if (!tag.equals("")) {
        fragmentManager.beginTransaction().add(R.id.contentFrame,fragment,tag).commit();
    } else {
        fragmentManager.beginTransaction().show(fragment).commit();
    }
}

Set up in onCreate():

currentFragmentTag = FragmentTag.NO_FRAGMENT;
if (lgsFragment == null) {
    lgsFragment = new LGSFragment();
    openFragment(lgsFragment, FragmentTag.LGS.toString());
} else {
    openFragment(lgsFragment, "");
}
currentFragmentTag = FragmentTag.LGS;
like image 20
Dennis van Opstal Avatar answered Sep 14 '25 04:09

Dennis van Opstal