2

I have a BottomNavigationView and I am using a NavController to switch menus and move the screen.

I would like to know how to keep a specific screen even when moving the menu freely.

Let me explain in more detail.

There are bottom menus A, B, C. And each has a Fragment screen.

All menus and screen transitions use NavHost, NavController, and nav_graph.

In menu C, a dialog is displayed through the button on the C screen.

The dialog is also linked to the nav_graph.

When an option is selected in the dialog, it moves to screen D.

D screen is the page where you write something.

This is important from now on.

The current menu is C, screen D is open, and something is being written.

However, if you go to another menu while writing and then return to C, screen C of the first menu C appears, not the screen you are writing.

Here, I want the screen I was writing to continue to be displayed even when I return from another menu.

Any good way?

For reference, since I am using the mvvm pattern and viewmodel, the data of the screen I am writing seems to be maintained.

Thank you.

nav_graph

<?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/nav_graph"
    app:startDestination="@id/calendar">

    <!-- menu C screen   -->
    <fragment
        android:id="@+id/write_home"
        android:name="com.example.writeweight.fragment.WriteRoutineHomeFragment"
        android:label="fragment_write_routine_home"
        tools:layout="@layout/fragment_write_routine_home" >
        <action
            android:id="@+id/action_write_home_to_bodyPartDialog"
            app:destination="@id/bodyPartDialog" />
    </fragment>

    <!-- dialog -->
    <dialog
        android:id="@+id/bodyPartDialog"
        android:name="com.example.writeweight.fragment.BodyPartDialogFragment"
        android:label="BodyPartDialogFragment"
        tools:layout="@layout/fragment_body_part_dialog">
        <action
            android:id="@+id/action_bodyPartDialog_to_write"
            app:destination="@id/write"/>
    </dialog>

    <!-- screen D (write page) -->
    <fragment
        android:id="@+id/write"
        android:name="com.example.writeweight.fragment.WriteRoutineFragment"
        android:label="WritingRoutineFragment"
        tools:layout="@layout/fragment_writing_routine">
        <action
            android:id="@+id/action_write_to_workoutListTabFragment"
            app:destination="@id/workoutListTabFragment" />
        <argument
            android:name="title"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="@null"/>
        <argument
            android:name="workout"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="@null"/>
    </fragment>
</navigation>
ybybyb
  • 1,385
  • 1
  • 12
  • 33
  • 1
    I don't really like this multiple navigation graph files approach given on most of such answers, I find it a hack as well instead of a proper solution. Basically, you want to maintain the state of the fragments. Two ways to do this: First one is easy and more popular, use a ViewPager/2. It will provide the functionality you want without messing up with things. Second, if you want to stick with NavGraph, then, create a custom Navigator to hide the fragment overriding the FragmentManager instead of replacing the fragment. Second one is a little complicated but gives more control. – Lalit Fauzdar Jul 24 '21 at 21:09
  • 1
    Does this answer your question? [Stop fragment refresh in bottom nav using navhost](https://stackoverflow.com/questions/60069786/stop-fragment-refresh-in-bottom-nav-using-navhost) – Lalit Fauzdar Jul 24 '21 at 21:12
  • @LalitFauzdar Thanks for suggesting different ways. Let me ask you a question about each of your two answers. In the `first answer` you said to use `ViewPager2`. By the way, I'm using `BottomNav`, isn't this more like a `Tab` than a `Viewpager`? Does it make a difference if the `menu` is at the `top` or at the `bottom`? Is there any reason it's a `ViewPager`? In the `second answer`, are you saying that I should make my own `Navigator` that uses `show/hide` method instead of `replace`? To be honest, I have no idea what you're talking about. It's hard. – ybybyb Jul 25 '21 at 16:11
  • @LalitFauzdar Oh, I saw the link now. Is the link the solution to your `answer 2`? – ybybyb Jul 25 '21 at 16:19
  • 1
    For the suggestion of `ViewPager`/`ViewPager2`, it is used in place of `fragment` in the layout and it can hold multiple fragments in the memory at a time which maintains the current state of the fragments and it can be used with `TabView`, `BottomNav`, `NavigationDrawer` or without any other widget, so when you switch to other tab on `BottomNavigationBar`, it shifts to other tab but maintains the first's state, it's done by setting the [`offscreenPageLimit`](https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2#OFFSCREEN_PAGE_LIMIT_DEFAULT). – Lalit Fauzdar Jul 25 '21 at 18:28
  • For the second one, it's not hard but limited. The [second answer](https://stackoverflow.com/a/64142496/8244632) of the link I've provided will help you achieving the solution without shifting to any other thing. Check that out. – Lalit Fauzdar Jul 25 '21 at 18:35
  • So if I want to use a 'ViewPager' then I have to use 'ViewPager' instead of 'Fragment' in layout? Of course, the bottom is set to 'BottomNavigationView'. And the second answer I said was about the second method of the first answer you wrote (about how to make a 'show/hide Navigator'). Sorry to confuse you. – ybybyb Jul 26 '21 at 16:07

1 Answers1

1

There are two ways to fix this issue either use version_navigation 2.4.0-alpha01 which is the easiest way or use NavigationExtensions to use NavigationExtensions you have to add this to your project NavigationExtensions and then in the navigation menu create separate navigation files for each tab. In the main activity, layout replace fragment with FragmentContainerView and remove NavHostFragment.

In the Mainactivty

class MainActivity : AppCompatActivity(), NavController.OnDestinationChangedListener {
private val viewModel by viewModels<MainViewModel>()
private var currentNavController: LiveData<NavController>? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)
    setSupportActionBar(toolbar)

    /*
    appBarConfiguration = AppBarConfiguration(
        // navController.graph,
        setOf(
             R.id.navigate_home, R.id.navigate_collection, R.id.navigate_profile
        ),
        drawerLayout
    )

    setupActionBarWithNavController(navController, appBarConfiguration)
    navView.setupWithNavController(navController)
    bottomNavView.setupWithNavController(navController)

    // make sure appbar/toolbar is not hidden upon fragment switch
    navController.addOnDestinationChangedListener { controller, destination, arguments ->
        if (destination.id in bottomNavDestinationIds) {
            appBarLayout.setExpanded(true, true)
        }
    }
     */
     // Add your tab fragments

    val navGraphIds = listOf(R.navigation.home, R.navigation.albumlist, R.navigation.test)
    val controller = bottomNavView.setupWithNavController(
        navGraphIds = navGraphIds,
        fragmentManager = supportFragmentManager,
        containerId = R.id.nav_host_container,
        intent = intent
    )
    // Whenever the selected controller changes, setup the action bar.
    controller.observe(this, Observer { navController ->

        setupActionBarWithNavController(navController)
        // optional NavigationView for Drawer implementation
        // navView.setupWithNavController(navController)

        addOnDestinationChangedListener(navController)
    })
    currentNavController = controller
}

private fun addOnDestinationChangedListener(navController: NavController) {
    // ensure only one listener is active
    navController.removeOnDestinationChangedListener(this)
    navController.addOnDestinationChangedListener(this)
}

override fun onDestinationChanged(
    controller: NavController,
    destination: NavDestination,
    arguments: Bundle?
) {
    if (destination.id in bottomNavDestinationIds) {
        appBarLayout.setExpanded(true, true)
    }
}}
Sadegh.t
  • 205
  • 3
  • 10
  • I also looked into `navigation 2.4.0-alpha01`. But I don't know if I'm doing something wrong, but I don't know what's different. – ybybyb Jul 25 '21 at 16:57
  • 1
    Check out this article https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f – Sadegh.t Jul 27 '21 at 09:38