I'm working on an android application, that uses a navigation drawer to switch between two fragments. However, each time I switch, the fragment is completely recreated.
Here is the code from my main activity.
/* The click listener for ListView in the navigation drawer */ private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); } } private void selectItem(int position) { android.support.v4.app.Fragment fragment; String tag; android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager(); switch(position) { case 0: if(fragmentManager.findFragmentByTag("one") != null) { fragment = fragmentManager.findFragmentByTag("one"); } else { fragment = new OneFragment(); } tag = "one"; break; case 1: if(fragmentManager.findFragmentByTag("two") != null) { fragment = fragmentManager.findFragmentByTag("two"); } else { fragment = new TwoFragment(); } tag = "two"; break; } fragment.setRetainInstance(true); fragmentManager.beginTransaction().replace(R.id.container, fragment, tag).commit(); // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); setTitle(mNavTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); }
I've set up some debug logging, and every time selectItem is called, one fragment is destroyed, while the other is created.
Is there any way to prevent the fragments from being recreated, and just reuse them instead?
After @meredrica pointed out that replace() destroys the fragments, I went back through the FragmentManager documentation. This is the solution I've come up with, that seems to be working.
/* The click listener for ListView in the navigation drawer */ private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); } } private void selectItem(int position) { android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager(); switch(position) { case 0: if(fragmentManager.findFragmentByTag("one") != null) { //if the fragment exists, show it. fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("one")).commit(); } else { //if the fragment does not exist, add it to fragment manager. fragmentManager.beginTransaction().add(R.id.container, new OneFragment(), "one").commit(); } if(fragmentManager.findFragmentByTag("two") != null){ //if the other fragment is visible, hide it. fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("two")).commit(); } break; case 1: if(fragmentManager.findFragmentByTag("two") != null) { //if the fragment exists, show it. fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("two")).commit(); } else { //if the fragment does not exist, add it to fragment manager. fragmentManager.beginTransaction().add(R.id.container, new TwoFragment(), "two").commit(); } if(fragmentManager.findFragmentByTag("one") != null){ //if the other fragment is visible, hide it. fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("one")).commit(); } break; } // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); setTitle(mNavTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); }
I also added this bit, but I'm not sure if it's necessary or not.
@Override public void onDestroy() { super.onDestroy(); FragmentManager fragmentManager = getSupportFragmentManager(); if(fragmentManager.findFragmentByTag("one") != null){ fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("one")).commit(); } if(fragmentManager.findFragmentByTag("two") != null){ fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("two")).commit(); } }
Use the attach/detach method with tags:
Detach will destroy the view hirachy but keeps the state, like if on the backstack; this will let the "not-visible" fragment have a smaller memory footprint. But mind you that you need to correctly implement the fragment lifecycle (which you should do in the first place)
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.
The first time you add the fragment
FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.add(android.R.id.content, new MyFragment(),MyFragment.class.getSimpleName()); t.commit();
then you detach it
FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.detach(MyFragment.class.getSimpleName()); t.commit();
and attach it again if switched back, state will be kept
FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName())); t.commit();
But you always have to check if the fragment was added yet, if not then add it, else just attach it:
if (getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()) == null) { FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.add(android.R.id.content, new MyFragment(), MyFragment.class.getSimpleName()); t.commit(); } else { FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName())); t.commit(); }
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