Stop fragment refresh in bottom nav using navhost
Asked Answered
L

15

26

This problem has been asked a few times now, but we are in 2020 now, did anyone find a good usable solution to this yet?

I want to be able to navigate using the bottom navigation control without refreshing the fragment each time they are selected. Here is what I have currently:

navigation/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    app:startDestination="@id/home">

    <fragment
        android:id="@+id/home"
        android:name="com.org.ftech.fragment.HomeFragment"
        android:label="@string/app_name"
        tools:layout="@layout/fragment_home" />
    <fragment
        android:id="@+id/news"
        android:name="com.org.ftech.fragment.NewsFragment"
        android:label="News"
        tools:layout="@layout/fragment_news"/>
    <fragment
        android:id="@+id/markets"
        android:name="com.org.ftech.fragment.MarketsFragment"
        android:label="Markets"
        tools:layout="@layout/fragment_markets"/>
    <fragment
        android:id="@+id/explore"
        android:name="com.org.ftech.ExploreFragment"
        android:label="Explore"
        tools:layout="@layout/fragment_explore"/>
</navigation>

activity_mail.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Use DrawerLayout as root container for activity -->
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/main" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:itemIconTint="@color/nav"
            app:itemTextColor="@color/nav"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:menu="@menu/main">

        </com.google.android.material.bottomnavigation.BottomNavigationView>

    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
        app:menu="@menu/main"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/navigationView"
        android:layout_gravity="start">
    </com.google.android.material.navigation.NavigationView>

</androidx.drawerlayout.widget.DrawerLayout>

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    private var drawerLayout: DrawerLayout? = null
    private var navigationView: NavigationView? = null
    private var bottomNavigationView: BottomNavigationView? = null
    private lateinit var appBarConfiguration: AppBarConfiguration

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        drawerLayout = findViewById(R.id.drawer_layout)
        navigationView = findViewById(R.id.navigationView)
        bottomNavigationView = findViewById(R.id.bottomNavigationView)

        val navController = findNavController(R.id.nav_host_fragment)
        appBarConfiguration = AppBarConfiguration(setOf(R.id.markets, R.id.explore, R.id.news, R.id.home), drawerLayout)

        setupActionBarWithNavController(navController, appBarConfiguration)

        findViewById<NavigationView>(R.id.navigationView)
            .setupWithNavController(navController)

        findViewById<BottomNavigationView>(R.id.bottomNavigationView)
            .setupWithNavController(navController)

    }


    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId == R.id.search) {
            startActivity(Intent(applicationContext, SearchableActivity::class.java))
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.options_menu, menu)
        return super.onCreateOptionsMenu(menu)
    }
}

In the fragment I am making a few calls to my services to fetch the data in onCreateView, when resuming the fragment I am assuming those calls will not longer be executed and the state of the fragment should be preserved.

Lateral answered 5/2, 2020 at 5:59 Comment(3)
what about using nav host for each item in bottom navigation and on clicking of the icon you change their visibility. I have done that and working perfectly.Northern
@Northern can you provide a sample on changing the visibility for those of us who use a nav host?Brinkema
check this answer, https://mcmap.net/q/183564/-stop-fragment-refresh-in-bottom-nav-using-navhost is an accepted answer.Incrust
B
13

Try this:

public class MainActivity extends AppCompatActivity {


    final Fragment fragment1 = new HomeFragment();
    final Fragment fragment2 = new DashboardFragment();
    final Fragment fragment3 = new NotificationsFragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

        fm.beginTransaction().add(R.id.main_container, fragment3, "3").hide(fragment3).commit();
        fm.beginTransaction().add(R.id.main_container, fragment2, "2").hide(fragment2).commit();
        fm.beginTransaction().add(R.id.main_container,fragment1, "1").commit();

    }


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

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

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

                case R.id.navigation_notifications:
                    fm.beginTransaction().hide(active).show(fragment3).commit();
                    active = fragment3;
                    return true;
            }
            return false;
        }
    };


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            startActivity(new Intent(MainActivity.this, SettingsActivity.class));
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


}

Or You can follow Google's recommended solution: Google Link

Bultman answered 13/2, 2020 at 6:19 Comment(7)
Many of us are using the nav Host xml where our fragments get defined. Any suggestions on applying this solution to that newer version since we wouldn't explicitly create fragments in the activity in code directlyBrinkema
@Brinkema what is you requirements? Please explain me.Bultman
I have a navigation xml which lists all of my fragments (following latest android pattern for navigation). My activity main xml uses a androidx.navigation.fragment.NavHostFragment element with the key value (app:navGraph="@navigation/my_nav"). The main activity does not create any fragments, only minor setup. findViewById<BottomNavigationView>(R.id.bottomNavigationView).setupWithNavController(findNavController(R.id.fragment)). The R.id.fragment is the NavHostFragment I mentioned earlier. This latest android pattern is different from your provided solution where fragments are manually createdBrinkema
@Brinkema he wants manual process. Btw if you want to more simplify please follow this link: simplifiedcoding.net/android-navigation-tutorialBultman
Yeah thats just a generic tutorial on how to set it up. It doesn't dive into how to ensure the fragment doesn't refreshBrinkema
Created a solution using google's recommended answer: https://mcmap.net/q/183564/-stop-fragment-refresh-in-bottom-nav-using-navhostBrinkema
Please any idea on how to upgrade to multiple backstacks? #68043091Anthropolatry
B
11

Kotlin 2020 Google's Recommended Solution

Many of these solutions call the Fragment constructor in the Main Activity. However, following Google's recommended pattern, this is not needed.

Setup Navigation Graph Tabs

Firstly create a navigation graph xml for each of your tabs under the res/navigation directory.

Filename: tab0.xml

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/tab0"
    app:startDestination="@id/fragmentA"
    tools:ignore="UnusedNavigation">

    <fragment
        android:id="@+id/fragmentA"
        android:label="@string/fragment_A_title"
        android:name="com.app.subdomain.fragA"
    >
    </fragment>
</navigation>

Repeat the above template for your other tabs. Important all fragments and the navigation graph has an id (e.g. @+id/tab0, @+id/fragmentA).

Setup Bottom Navigation View

Ensure the navigation ids are the same as the ones specified on the bottom menu xml.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:title="@string/fragment_A_title"
        android:id="@+id/tab0"
        android:icon="@drawable/ic_baseline_book_24"/>

    <item android:title="@string/fragment_B_title"
        android:id="@+id/tab1"
        android:icon="@drawable/ic_baseline_add_alert_24"/>

    <item android:title="@string/fragment_C_title"
        android:id="@+id/tab2"
        android:icon="@drawable/ic_baseline_book_24"/>

    <item android:title="@string/fragment_D_title"
        android:id="@+id/tab3"
        android:icon="@drawable/ic_baseline_more_horiz_24"/>

</menu>

Setup Activity Main XML

Ensure FragmentContainerView is being used and not <fragment and do not set the app:navGraph attribute. This will set later in code


<androidx.fragment.app.FragmentContainerView
      android:id="@+id/fragmentContainerView"
      android:name="androidx.navigation.fragment.NavHostFragment"
      android:layout_width="0dp"
      android:layout_height="0dp"
      app:defaultNavHost="true"
      app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@id/main_toolbar"
/>

Main Activity XML

Copy over the following Code into your main activity Kotlin file and call setupBottomNavigationBar within OnCreateView. Ensure you navGraphIds use R.navigation.whatever and not R.id.whatever

private lateinit var currentNavController: LiveData<NavController>

private fun setupBottomNavigationBar() {
  val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
  val navGraphIds = listOf(R.navigation.tab0, R.navigation.tab1, R.navigation.tab2, R.navigation.tab3)
  val controller = bottomNavigationView.setupWithNavController(
      navGraphIds = navGraphIds,
      fragmentManager = supportFragmentManager,
      containerId = R.id.fragmentContainerView,
      intent = intent
  )
  controller.observe(this, { navController ->
      val toolbar = findViewById<Toolbar>(R.id.main_toolbar)
      val appBarConfiguration = AppBarConfiguration(navGraphIds.toSet())
      NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
      setSupportActionBar(toolbar)
  })
  currentNavController = controller
}

override fun onSupportNavigateUp(): Boolean {
  return currentNavController?.value?.navigateUp() ?: false
}

Copy NavigationExtensions.kt File

Copy the following file to your codebase

[EDIT] The above link is broken. Found it in a forked repo

Source

Brinkema answered 30/9, 2020 at 17:8 Comment(6)
This is the right way to go in 2020, I am using this navigation component in my application now.Lateral
I have followed your guide, very helpful. Unfortunately navigation works exactly the same as before, all fragments are being recreated. Any suggestions or did I misunderstood what the google solution was supposed to do.Contravallation
Can you provide this NavigationExtensions.kt File, given link is brokenFaculty
@AnzyShQ Thank you for the comment. Found it in a forked repo. Updated AnswerBrinkema
don't use this way, you need the update libs. see this answer https://mcmap.net/q/183564/-stop-fragment-refresh-in-bottom-nav-using-navhostIncrust
@RasoulMiri Although the new alpha version may fix it, it's still an alpha release. They keep publishing a new alpha for this version every 2 weeks. Just keep that in mind when deploying alpha changes with your production code. I am happy to see Google has addressed at least looked into this matterBrinkema
B
11

The simple solution to stop refreshing on multiple clicks on the same navigation item could be

 binding.navView.setOnNavigationItemSelectedListener { item ->
        if(item.itemId != binding.navView.selectedItemId)
            NavigationUI.onNavDestinationSelected(item, navController)
        true
    }

where binding.navView is the reference for BottomNavigationView using Android Data Binding.

Banter answered 7/12, 2020 at 3:57 Comment(2)
In this case you can do simply binding.navView.setOnNavigationItemReselectedListener { }Koch
You can use navView.setOnItemReselectedListener { } instead of deprecated navView.setOnNavigationItemReselectedListener { }Demibastion
S
6

If you are using Jetpack, the easiest way to solve this is using ViewModel

You have to save all valuable data and not make unnecessary database loads or network calls everytime you go to a fragment from another.

UI controllers such as activities and fragments are primarily intended to display UI data, react to user actions, or handle operating system communication, such as permission requests.

Here is when we use ViewModels

ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance.

So if the fragment is recreated, all your data will be there instantly instead of make another call to database or network. Its important to know that if the activity or fragment that holds the ViewModel is reacreated, you will receive the same ViewModel instance created before.

But in this case you have to specify the ViewModel to have activity scope instead of fragment scope, independently if you are using a shared ViewModel for all the fragments, or a different ViewModel for every fragment.

Here is a little example using LiveData too:

//Using KTX
val model by activityViewModels<MyViewModel>()
model.getData().observe(viewLifecycleOwner, Observer<DataModel>{ data ->
        // update UI
    })

//Not using KTX
val model by lazy {ViewModelProvider(activity as ViewModelStoreOwner)[MyViewModel::class.java]}
model.getData().observe(viewLifecycleOwner, Observer<DataModel>{ data ->
        // update UI
    })

And that's it! Google is actively working on multiple back stack support for bottom tab Navigation and claim that it'll arrive on Navigation 2.4.0 as said here and on this issue tracker if you want and/or your problem is more related to multiple back stack, you can check out those links

Remember fragments still be recreated, usually you don't change component behavior, instead, you adapt your data to them!

I leave you some useful links:

ViewModel Overview Android Developers

How to communicate between fragments and activities with ViewModels - on Medium

Restoring UI State using ViewModels - on Medium

Sheela answered 20/5, 2020 at 21:16 Comment(2)
What if our bottom navigation View fragment keeps showing full screen fragments? Lets say 3 levels deep for that single tab. Then wouldn't you need a viewModel essentially recording which fragment the user is on? And then quickly recreate these fragments on the fly when it gets created again? Seems like a lot of work and needing to mess around with navigation stacks which is less than idealBrinkema
the docs say this is the way to go. though im not really sure if it is. i tried a simple int tracking, it always resets back to its initial value instead of the incremented one. the docs itself is fairly limited it does not even provide a simple working example.Contrary
S
4

Quick tip, if you just want to prevent loading the already selected fragment just override setOnNavigationItemReselectedListener and do nothing, but this won't save the fragment states

binding.navBar.setOnNavigationItemReselectedListener {  }
Straus answered 6/2, 2021 at 17:18 Comment(0)
I
3

You use the old version, you just use version 2.4.0-alpha05 or above. This answer is updated in 2021.

androidx.navigation:navigation-runtime-ktx:2.4.0-alpha05
androidx.navigation:navigation-fragment-ktx:2.4.0-alpha05
androidx.navigation:navigation-ui-ktx:2.4.0-alpha05
Incrust answered 25/9, 2021 at 9:58 Comment(2)
Does this implementations only stops the fragment refresh ? Seems not working. Can you provide any snippet or something working ?Faculty
this works, thanksObliteration
W
2

If you use NavigationUI.setupWithNavController(), the NavOptions are defined for you with NavigationUI.onNavDestinationSelected(). These options include launchSingleTop and, if the menu item is not secondary, a popUpTo the root of the graph.

The problem is, that launchSingleTop still replaces the top fragment with a new one. To resolve this issue, you'd have to create your own setupWithNavController() and onNavDestinationSelected() functions. In onNavDestinationSelected() you'd just adapt the NavOptions to your needs.

Word answered 8/2, 2020 at 13:42 Comment(2)
Do you have some working sample that demonstrates this approach? I was looking at here and here, would these solve this problem?Lateral
I actually replaced the fragment navigator to only show and hide the fragments. Before I tried to do what I suggested here and it worked as well. But it was a different use-case. You could also use actions to define the behavior then.Word
H
1

If you are using navigation component,In addition to this answer From version:'2.4.0-alpha01' it has inbuilt support for multiple back stacks.So no navigation extension is needed

refer to this link for more details. https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f

Hackberry answered 11/8, 2021 at 9:15 Comment(0)
A
0

Try something like this

navView.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)

And

private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
    when (item.itemId) {
        R.id.home -> {
            fragmentManager.beginTransaction().hide(active).show(homeFragment).commit()
            active = homeFragment
            return@OnNavigationItemSelectedListener true
        }
        R.id.news -> {
            fragmentManager.beginTransaction().hide(active).show(newsFragment).commit()
            active = newsFragment
            return@OnNavigationItemSelectedListener true
        }
        R.id.markets -> {
            fragmentManager.beginTransaction().hide(active).show(marketsFragment).commit()
            active = marketsFragment
            return@OnNavigationItemSelectedListener true
        }
        R.id.explore -> {
            fragmentManager.beginTransaction().hide(active).show(exploreFragment).commit()
            active = exploreFragment
            return@OnNavigationItemSelectedListener true
        }
    }
    false
}
Accustom answered 5/2, 2020 at 11:12 Comment(4)
Do I need to set the fragments in supportFragmentManager before I try to navigate between them in a listener?Lateral
Yeah. Set all of them in OnCreate(...) of your Activity. Make sure default is Active & rest others are HiddenAccustom
OnViewCreated will be executed either way. It will always be called distegarding how you are using fragmentManager - classic way or via jetpack navigation.Builtin
Make sure you get the references to your fragments by first checking for their existence via FindFragmentByTag, and that you track the active fragment tag using onSaveInstanceState / onCreate(Bundle)Fisken
A
0

create a class:

@Navigator.Name("keep_state_fragment") // `keep_state_fragment` is used in navigation xml 
class KeepStateNavigator(
private val context: Context,
private val manager: FragmentManager, // Should pass childFragmentManager.
private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

override fun navigate(
    destination: Destination,
    args: Bundle?,
    navOptions: NavOptions?,
    navigatorExtras: Navigator.Extras?
): NavDestination? {
    val tag = destination.id.toString()
    val transaction = manager.beginTransaction()

    var initialNavigate = false
    val currentFragment = manager.primaryNavigationFragment
    if (currentFragment != null) {
        transaction.detach(currentFragment)
    } else {
        initialNavigate = true
    }

    var fragment = manager.findFragmentByTag(tag)
    if (fragment == null) {
        val className = destination.className
        fragment = manager.fragmentFactory.instantiate(context.classLoader, className)
        transaction.add(containerId, fragment, tag)
    } else {
        transaction.attach(fragment)
    }

    transaction.setPrimaryNavigationFragment(fragment)
    transaction.setReorderingAllowed(true)
    transaction.commitNow()

    return if (initialNavigate) {
        destination
    } else {
        null
    }
}
}

Use keep_state_fragment instead of fragment in nav_graph

In Activity:

val navController = findNavController(R.id.nav_host_fragment)

    // get fragment
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!
    // setup custom navigator
    val navigator = KeepStateNavigator(this, navHostFragment.childFragmentManager, R.id.nav_host_fragment)
    navController.navigatorProvider += navigator

    // set navigation graph
    navController.setGraph(R.navigation.nav_graph)
    bottom_navigation.setupWithNavController(navController)
Alesiaalessandra answered 25/4, 2020 at 18:15 Comment(1)
This doesn't work. The fragment is always null and so a new instance is created even though I used <keep_state_fragment> inside navGraph. Seemed an elegant approach, but not working :/ Besides, navController.navigatorProvider gives warning: NavController.setNavigatorProvider can only be called from within the same library group (groupId=androidx.navigation)Havstad
R
0

Use this snippet:

private fun attachFragment(fragmentTag: String) {
    val fragmentTransaction = supportFragmentManager.beginTransaction()
    supportFragmentManager.findFragmentByTag(fragmentTag)?.let {
        if (supportFragmentManager.backStackEntryCount == 0) return
        val currentFragmentTag = supportFragmentManager.getBackStackEntryAt(supportFragmentManager.backStackEntryCount - 1).name
        (supportFragmentManager.findFragmentByTag(currentFragmentTag) as? FragmentBase)?.let { curFrag ->
            fragmentTransaction.hide(curFrag)
        }
        fragmentTransaction.show(it)
    } ?: run {
        when (fragmentTag) {
            FragmentHome.TAG -> FragmentBase.newInstance<FragmentHome>()
            FragmentAccount.TAG -> FragmentBase.newInstance<FragmentAccount>()
            else -> null
        }?.let {
            fragmentTransaction.add(R.id.container, it, fragmentTag)
            fragmentTransaction.addToBackStack(fragmentTag)
        }
    }
    fragmentTransaction.commit()
}

You can use this pass the tag of specific fragment that you want to show now, using method attachFragment(FragmentHome.TAG)

Radiator answered 8/10, 2020 at 10:56 Comment(0)
E
0

Hi friend, it's new solution:

BottomNavigationView navView = findViewById(R.id.nav_view);

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);

binding.navView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                if (item.getItemId() != binding.navView.getSelectedItemId())
                    NavigationUI.onNavDestinationSelected(item, navController);
                return true;
            }
        });
Evacuation answered 1/12, 2021 at 7:52 Comment(0)
K
0

I was looking for the best way to handle this and finally i came out with this simple idea : deactivate the MenuItem currently selected.

This way, you cannot click twice on it and therefore reloading the fragment is prevented.

Don't forget to Enable it back when you go to another fragment through your navHost.

The mechanic is based on the NavHostFragment which receives your BottomNavigationView from within a fragment/activity.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    navHostFragment =
        childFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

    val navView: BottomNavigationView = view.findViewById(R.id.nav_view)
    val navController = navHostFragment.navController

    //Here you link the NavHostFragment's navController to your 
    //bottomMenu
    navView.setupWithNavController(navController)

    //Add a listener monitoring the destination changes
    navController.addOnDestinationChangedListener(object : NavController.OnDestinationChangedListener{
        override fun onDestinationChanged(
            controller: NavController,
            destination: NavDestination,
            arguments: Bundle?
        ) {
            /* Disable the selected item and re-enable the others */
            for( item in navView.menu.iterator()){
                item.isEnabled = item.itemId != navView.selectedItemId
            }
        }

    })
}

Hoping it might help

Lenzy

Killion answered 11/3, 2022 at 17:12 Comment(0)
P
0

Try this: For ChipNavigationBar

private void Bottom_navigation() {
        final Fragment fragment1 = new home_fragment();
        final Fragment fragment2 = new bottom_nav1_Bookmark_Fragment();
        final Fragment fragment3 = new bottom_nav1_Search_Fragment();
        final FragmentManager fm = getSupportFragmentManager();

 ChipNavigationBar chipNavigationBar = findViewById(R.id.chipNavigation);
        chipNavigationBar.setItemSelected(R.id.home, true);


        fm.beginTransaction().add(R.id.frame, fragment3, "3").hide(fragment3).commit();
        fm.beginTransaction().add(R.id.frame, fragment2, "2").hide(fragment2).commit();
        fm.beginTransaction().add(R.id.frame,fragment1, "1").commit();

        chipNavigationBar.setOnItemSelectedListener(new ChipNavigationBar.OnItemSelectedListener() {
            @Override
            public void onItemSelected(int i) {
                Fragment active = fragment1;
                Fragment fragment = null;
                switch (i) {
                    case R.id.home:
                        fm.beginTransaction().hide(active).show(fragment1).commit();
                        active = fragment1;
                        chipNavigationBar.animate().translationY(0);

                        break;
                    case R.id.tajbi:

                        fm.beginTransaction().hide(active).show(fragment2).commit();
                        active = fragment2;

                        break;
                    case R.id.more_App:
                        fm.beginTransaction().hide(active).show(fragment3).commit();
                        active = fragment3;
                        
                        break;
                }
            }
        });
    }
Parson answered 16/7, 2022 at 11:30 Comment(0)
S
0

You can resolve this issue by using this solution.

First, declare fragments that are used in the bottom navigation view.

val fragment1: Fragment = HomeFragment()
val fragment2: Fragment = ProfileFragment()
val fragment3: Fragment = SettingsFragment()
val fm: FragmentManager = supportFragmentManager
var active = fragment1

Now you need to setup the bottom navigation like this

  private fun setUpBottomNavigation() {
    fm.beginTransaction().add(R.id.mainHostFragment, fragment3, "3").hide(fragment3).commit();
    fm.beginTransaction().add(R.id.mainHostFragment, fragment2, "2").hide(fragment2).commit();
    fm.beginTransaction().add(R.id.mainHostFragment,fragment1, "1").commit();
    val navigation = findViewById<View>(R.id.bottomNavigationView) as BottomNavigationView
    navigation.setOnItemSelectedListener(mOnNavigationItemSelectedListener)
}

private val mOnNavigationItemSelectedListener =
    NavigationBarView.OnItemSelectedListener  { item ->
        when (item.itemId) {
            R.id.homeFragment -> {
                supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                fm.beginTransaction().hide(active).show(fragment1).commit()
                active = fragment1
            }
            R.id.assessmentListFragment -> {
                supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                fm.beginTransaction().hide(active).show(fragment2).commit()
                active = fragment2
            }
            R.id.settingsFragment -> {
                supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                fm.beginTransaction().hide(active).show(fragment3).commit()
                active = fragment3
            }
        }
        false
    }
Strega answered 23/1, 2023 at 13:54 Comment(1)
How can i achieve to mark bottom navigation view item selected?Moller

© 2022 - 2024 — McMap. All rights reserved.