Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I switch between two fragments, without recreating the fragments each time?

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?

like image 363
Tester101 Avatar asked Mar 28 '14 12:03

Tester101


2 Answers

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();     } } 
like image 145
Tester101 Avatar answered Sep 23 '22 14:09

Tester101


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(); } 
like image 42
Patrick Favre Avatar answered Sep 22 '22 14:09

Patrick Favre