Fragment re-created on bottom navigation view item selected

AndroidAndroid FragmentsAndroid Support-LibraryBottomnavigationview

Android Problem Overview


Following is my code for bottom navigation view item selected

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {  
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Fragment fragment = null;
    switch (item.getItemId()) {
        case R.id.action_one:
            // Switch to page one
            fragment = FragmentA.newInstance();
            break;
        case R.id.action_two:
            // Switch to page two
            fragment = FragmentB.newInstance();
            break;
        case R.id.action_three:
            // Switch to page three
            fragment = FragmentC.newInstance();
            break;
    }
    getSupportFragmentManager().beginTransaction().replace(R.id.container,fragment,"TAG").commit();
    return true;
}
});

Now my problem is every time fragment is re-created and don't want fragment to be recreated every time I also tried adding addToBackStack(null) but it this case on back button press keeps popping fragments from stack which I don't want.

Is there any way to display fragments on bottom navigation bar selected without re-creating fragment

Android Solutions


Solution 1 - Android

With support library v26 you can do this

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

Fragment curFrag = mFragmentManager.getPrimaryNavigationFragment();
if (curFrag != null) {
    fragmentTransaction.detach(curFrag);
}

Fragment fragment = mFragmentManager.findFragmentByTag(tag);
if (fragment == null) {
    fragment = new YourFragment();
    fragmentTransaction.add(container.getId(), fragment, tag);
} else {
    fragmentTransaction.attach(fragment);
}

fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();

Solution 2 - Android

I faced the same problem and finally i i found the solution, you can try this code. it's work for me.

import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
public BottomNavigationView bv;
public home_fragment home;
public account_fragment afrag;
public other_fragment other;
public FrameLayout fr;
android.support.v4.app.Fragment current;
//public FragmentTransaction frt;
    public static int temp=0;
    final Fragment fragment11 = new account_fragment();
    final Fragment fragment22 = new home_fragment();
    final Fragment fragment33 = new other_fragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment11;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bv=(BottomNavigationView) findViewById(R.id.navigationView);
     
    
        fm.beginTransaction().add(R.id.main_frame, fragment33, "3").hide(fragment33).commit();
        fm.beginTransaction().add(R.id.main_frame, fragment22, "2").hide(fragment22).commit();
        fm.beginTransaction().add(R.id.main_frame,fragment11, "1").commit();
bv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.account:
                        fm.beginTransaction().hide(active).show(fragment11).commit();
                        active = fragment11;
                        return true;

                    case R.id.home1:
                        fm.beginTransaction().hide(active).show(fragment22).commit();
                        active = fragment22;
                        return true;

                    case R.id.other:
                        fm.beginTransaction().hide(active).show(fragment33).commit();
                        active = fragment33;
                        return true;
                }
                return false;
            }
        });
      bv.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
          @Override
          public void onNavigationItemReselected(@NonNull MenuItem item) {
              Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();

          }
      });


    }
 
}

Solution 3 - Android

This seemed to work well for me. Instead of attaching and detaching, i use show or hide to maintain fragment state.

    public void changeFragment(Fragment fragment, String tagFragmentName) {

        FragmentManager mFragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

        Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
        if (currentFragment != null) {
            fragmentTransaction.hide(currentFragment);
        }

        Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
        if (fragmentTemp == null) {
            fragmentTemp = fragment;
            fragmentTransaction.add(R.id.frame_layout, fragmentTemp, tagFragmentName);
        } else {
            fragmentTransaction.show(fragmentTemp);
        }

        fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
        fragmentTransaction.setReorderingAllowed(true);
        fragmentTransaction.commitNowAllowingStateLoss();
    }

And this is how i use it

     private void initViews() {
        BottomNavigationView bottomNavigationView = findViewById(R.id.navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener
                (new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        Fragment selectedFragment = null;
                        switch (item.getItemId()) {
                            case R.id.explore:
                                changeFragment(new ExploreFragment(), ExploreFragment.class
                                        .getSimpleName());
                                toggleViews(true, "");
                                break;
                            case R.id.favorite:
                                changeFragment(new FavoriteFragment(), FavoriteFragment.class
                                        .getSimpleName());
                                toggleViews(false, "Favorites");
                                break;
                            case R.id.venue:
                                changeFragment(new VenueFragment(), VenueFragment.class.getSimpleName());
                                toggleViews(false, "Venues");
                                break;
                            case R.id.profile:
                                changeFragment(new ProfileFragment(), ProfileFragment.class
                                        .getSimpleName());
                                toggleViews(false, "Profile");
                                break;
                        }
                        return true;
                    }
                });

        //Manually displaying the first fragment - one time only
        changeFragment(new ExploreFragment(), ExploreFragment.class
                .getSimpleName());

    }

Solution 4 - Android

This solution stops a fragment from being re-created when the currently selected nav button is clicked again. However, a new fragment will be created every time the user clicks a different nav button.

Just add this line to avoid re-created Fragment from BottomNavigationView

 bottomNavigation.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
            @Override
            public void onNavigationItemReselected(@NonNull MenuItem item) {
             // do nothing here   
            }
        });

Or with a lambda:

bottomNavigation.setOnNavigationItemReselectedListener(item -> { });

Solution 5 - Android

Be careful when using replace. Even if providing a fragment that already exists in memory, replace will restart the fragment's lifecycle. To avoid a restart, the transaction object's methods includes add, show, and hide, which can be used to show the correct fragment without restarting it.

private fun switchFragment(selectedTabIndex: Int) {
    val previousTabIndex = this.currentTabIndex
    this.currentTabIndex = selectedTabIndex

    val transaction = supportFragmentManager.beginTransaction()
    val tag = fragments[this.currentTabIndex].tag

    // if the fragment has not yet been added to the container, add it first
    if (supportFragmentManager.findFragmentByTag(tag) == null) {
        transaction.add(R.id.container, fragments[this.currentTabIndex], tag)
    }

    transaction.hide(fragments[previousTabIndex])
    transaction.show(fragments[this.currentTabIndex])
    transaction.commit()
}

Solution 6 - Android

Try this :

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
				@Override
				public boolean onNavigationItemSelected(@NonNull MenuItem item) {
					Fragment fragment = null;
					Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
					switch (item.getItemId()) {
						case R.id.action_one:
							// Switch to page one
							if (!(currentFragment instanceof FragmentA)) {
								fragment = FragmentA.newInstance();
							}
							break;
						case R.id.action_two:
							// Switch to page two
							if (!(currentFragment instanceof FragmentB)) {
								fragment = FragmentB.newInstance();
							}
							break;
						case R.id.action_three:
							// Switch to page three
							if (!(currentFragment instanceof FragmentC)) {
								fragment = FragmentC.newInstance();
							}
							break;
					}
					getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "TAG").commit();
					return true;
				}
			});

That will get the current fragment in your container and if you click again on this fragment that will not re add the fragment.

Solution 7 - Android

setOnNavigationItemReselectedListener would be a better solution for that

Solution 8 - Android

Use setOnNavigationItemReselectedListener like this:

private BottomNavigationView.OnNavigationItemReselectedListener onNavigationItemReselectedListener
            = new BottomNavigationView.OnNavigationItemReselectedListener() {

        @Override
        public void onNavigationItemReselected(@NonNull MenuItem item) {
            Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();
        }
    };

and call it using:

navigation.setOnNavigationItemReselectedListener(onNavigationItemReselectedListener);

Solution 9 - Android

The best way i found to do it.

private void replace_fragment(Fragment fragment) {

        String tag = fragment.getClass().getSimpleName();
        FragmentTransaction tr = getSupportFragmentManager().beginTransaction();

        Fragment curFrag = getSupportFragmentManager().getPrimaryNavigationFragment();
        Fragment cacheFrag = getSupportFragmentManager().findFragmentByTag(tag);

        if (curFrag != null)
            tr.hide(curFrag);

        if (cacheFrag == null) {
            tr.add(R.id.main_frame, fragment, tag);
        } else {
            tr.show(cacheFrag);
            fragment = cacheFrag;
        }

        tr.setPrimaryNavigationFragment(fragment);
        tr.commit();

    }
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {

            switch (item.getItemId()) {
                case R.id.nav_posts:
                    replace_fragment(new PostsFragment());
                    return true;
                case R.id.nav_stores:
                    replace_fragment(new StoresFragment());
                    return true;
                case R.id.nav_chats:
                    replace_fragment(new DiscussionsFragment());
                    return true;
                case R.id.nav_account:
                    replace_fragment(new ProfileFragment());
                    return true;
            }
            return false;
        }
    };

Solution 10 - Android

I've improved @Viven's answers and written it with Kotlin. My version instantiates fragment only for the first time, hides/shows. I'm new at Kotlin so tell me if I can improve anything.

We need to hold a id to tag map:

private val fragmentTags = hashMapOf(
        R.id.action_home to "home_fragment",
        R.id.action_profile to "profile_fragment"
)

The listener code:

bottomNavigation.run {
    setOnNavigationItemSelectedListener { menuItem ->
        supportFragmentManager.beginTransaction()
                .let { transaction ->
                    // hide current fragment
                    supportFragmentManager.primaryNavigationFragment?.let {
                        // if selected fragment's tag is same, do nothing.
                        if (it.tag == fragmentTags[menuItem.itemId]) {
                            return@setOnNavigationItemSelectedListener true
                        }

                        transaction.hide(it)
                    }

                    var fragment  = supportFragmentManager.findFragmentByTag(fragmentTags[menuItem.itemId])

                    if (fragment == null) {
                        // instantiate fragment for the first time
                        fragment = when(menuItem.itemId) {
                            R.id.action_home -> HomeFragment()
                            R.id.action_profile -> ProfileFragment()
                            else -> null
                        }?.also {
                            // and add it 
                            transaction.add(R.id.frame, it, fragmentTags[menuItem.itemId])
                        }
                    } else {
                        // if it's found show it 
                        transaction.show(fragment)
                    }


                    transaction
                            .setPrimaryNavigationFragment(fragment)
                            .setReorderingAllowed(true)
                }.commitNowAllowingStateLoss()

        return@setOnNavigationItemSelectedListener true
    }

    //bottomNavigation things
    itemIconTintList = null
    selectedItemId = R.id.action_home
}

Solution 11 - Android

I solved this problem by adding a ViewPager to which I delegated all my navigation fragments. Its adapter (FragmentPagerAdapter) doesn't recreate the fragments instances when the user navigates through the BotoomNavigationView.

To achieve this, you have to complete 5 easy steps:

  1. add a ViewPager to your layout;

  2. implement its adapter:

    class YourNavigationViewPagerAdapter(fm: FragmentManager,
                                         private val param1: Int,
                                         private val param2: Int)
    
        : FragmentPagerAdapter(fm) {
    
        override fun getItem(p0: Int) = when(p0) {
            0 -> NavigationFragment1.newInstance(param1, param2)
            1 -> NavigationFragment2.newInstance(param1, param2)
            2 -> NavigationFragment3.newInstance(param1, param2)
            else -> null
        }
    
        override fun getCount() = 3
    }
    
  3. don't forget to set the new adapter:

    yourViewPager.adapter = YourNavigationViewPagerAdapter(supportFragmentManager, param1, param2)
    
  4. set a OnNavigationItemSelectedListener to your BottomNavigationView like the following:

    yourBottomNavigationView.setOnNavigationItemSelectedListener {
    
        when(it.itemId) {
    
            R.id.yourFirstFragmentMenuItem -> {
                yourViewPager.currentItem = 0
                true
            }
    
            R.id.yourSecondFragmentMenuItem -> {
                yourViewPager.currentItem = 1
                true
            }
    
            R.id.yourThirdFragmentMenuItem -> {
                yourViewPager.currentItem = 2
                true
            }
    
    
            else -> false
        }
    }
    
  5. set a OnPageChangeListener to your ViewPager like the following:

    yourViewPager.addOnPageChangeListener(object : 
    ViewPager.OnPageChangeListener {
        override fun onPageScrollStateChanged(p0: Int) {
    
        }
    
        override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
    
        }
    
        override fun onPageSelected(p0: Int) {
            yourBottomNavigationView.menu.getItem(p0).isChecked = true
        }
    
    })
    
  6. enjoy :)

Solution 12 - Android

I wrote an Extension function in Kotlin for the same.

fun FragmentManager.switch(containerId: Int, newFrag: Fragment, tag: String) {

var current = findFragmentByTag(tag)
beginTransaction()
    .apply {

        //Hide the current fragment
        primaryNavigationFragment?.let { hide(it) }

        //Check if current fragment exists in fragmentManager
        if (current == null) {
            current = newFrag
            add(containerId, current!!, tag)
        } else {
            show(current!!)
        }
    }
    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
    .setPrimaryNavigationFragment(current)
    .setReorderingAllowed(true)
    .commitNowAllowingStateLoss()
}

Solution 13 - Android

There are several test cases involved in proper navigation , I am pasting my code with all test cases checked.

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        switch (item.getItemId()) {
            case R.id.dashboard:
                Fragment fragment;
                fragment = fragmentManager.findFragmentByTag(DashboardFragment.TAG);

                if (fragment == null) {
                    fragment = new DashboardFragment();
                    fragmentTransaction.add(R.id.frame, fragment, DashboardFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.expenses:
                fragment = fragmentManager.findFragmentByTag(ExpenseFragment.TAG);
                if (fragment == null) {
                    fragment = new ExpenseFragment();
                    fragmentTransaction.add(R.id.frame, fragment, ExpenseFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.vehicle_parts:
                Bundle bundle = new Bundle();
                bundle.putInt("odometer", vehicle.getOdometer());
                bundle.putString("vehicle_id", vehicle.get_id());
                fragment = fragmentManager.findFragmentByTag(PartsFragment.TAG);
                if (fragment == null) {
                    fragment = new PartsFragment();
                    fragment.setArguments(bundle);
                    fragmentTransaction.add(R.id.frame, fragment, PartsFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.blog:
                fragment = fragmentManager.findFragmentByTag(BlogFragment.TAG);

                if (fragment == null) {
                    fragment = new BlogFragment();
                    fragmentTransaction.add(R.id.frame, fragment, BlogFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
        }
        return false;

Solution 14 - Android

Use Cicerone library for handling navigation easily.

https://github.com/terrakok/Cicerone

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
    switch (menuItem.getItemId()) {
        case R.id.save: {
            router.replaceScreen("your fragment1");
            menuItem.setChecked(true);
            break;
        }
        case R.id.search_lessons: {
            router.replaceScreen("your fragment2");
            menuItem.setChecked(true);
            break;
        }
        case R.id.profile_student: {
            router.replaceScreen("your fragment3");
            menuItem.setChecked(true);
            break;
        }

    }
    return false;
}

Solution 15 - Android

How To stop recreating Fragment when it is Already Visible Using BottomNavigationView

Step 1--

     @Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Fragment fragment = null;
    String valuestring;      
    **if (item.getItemId() == lastSelectedItemId) { // added this
        Log.e( "both id is same","LULL" );
        return true;
    }**
    switch (item.getItemId()) {
       case R.id.navigation_category:
      SetActioBarText( getString( R.string.label_actionbar_categories ) );
            fragment = new CategoryFragment();
            valuestring = "category";
            break;
        case R.id.navigation_favourite:
     SetActioBarText( getString( R.string.label_actionbar_favourites ) );
            fragment = new FavouriteFragment();
            valuestring = "favourites";
            break;
        default:
            throw new IllegalStateException( "Unexpected value: " +                  menuItem.getItemId() );
    }
    return loadFragment( fragment, valuestring ,menuItem);
}

Now Step 2---

    private boolean loadFragment(Fragment fragment, String argument,MenuItem item) {
  
    if (fragment != null) {
       
        transaction = fragmentManager.beginTransaction();
        transaction.addToBackStack( argument );
        transaction.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_FADE );
        transaction.replace( R.id.frame_container, fragment,                         "demofragment").commitAllowingStateLoss();

     lastSelectedItemId= item.getItemId();
    
        return true;
    }
    return false;
}

Solution 16 - Android

no need to do this answers. actually what you really need is to save view in specific fragmet.

 private   View view;
if(view == null){
    view = inflater.inflate(R.layout.call_fragment_edited, container, false);

}

so any time when you create new fragment you see current state

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionamodkantheView Question on Stackoverflow
Solution 1 - AndroidVivenView Answer on Stackoverflow
Solution 2 - AndroidMazen JameelView Answer on Stackoverflow
Solution 3 - AndroidSteve KamauView Answer on Stackoverflow
Solution 4 - AndroidNikunj ParadvaView Answer on Stackoverflow
Solution 5 - AndroidMaxwellView Answer on Stackoverflow
Solution 6 - AndroidRaphael TeyssandierView Answer on Stackoverflow
Solution 7 - AndroidakaMaheshView Answer on Stackoverflow
Solution 8 - AndroidAkshay SahaiView Answer on Stackoverflow
Solution 9 - AndroidibrozzView Answer on Stackoverflow
Solution 10 - AndroidosrlView Answer on Stackoverflow
Solution 11 - Androidivan8m8View Answer on Stackoverflow
Solution 12 - AndroidSharukh MohammedView Answer on Stackoverflow
Solution 13 - AndroidHarshitGView Answer on Stackoverflow
Solution 14 - AndroidDavud DavudovView Answer on Stackoverflow
Solution 15 - AndroidSunil ChaudharyView Answer on Stackoverflow
Solution 16 - AndroidMohamad AlemiView Answer on Stackoverflow