How to change start destination of a navigation graph programmatically [Jetpack]

前端 未结 7 1766
醉酒成梦
醉酒成梦 2020-12-08 00:30

Basically, I have the following navigation graph:

I want to change my starting point in navigation graph to fragment 2 right after reaching it

相关标签:
7条回答
  • 2020-12-08 00:44

    For those who have a navigation xml file with similar content to this:

    <?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/mobile_navigation"
        app:startDestination="@+id/nav_home">
    
        <fragment
            android:id="@+id/nav_home"
            android:name="HomeFragment"
            android:label="@string/menu_home"
            tools:layout="@layout/fragment_home" />
    
        <fragment
            android:id="@+id/nav_users"
            android:name="UsersFragment"
            android:label="@string/users"
            tools:layout="@layout/fragment_users" />
    
        <fragment
            android:id="@+id/nav_settings"
            android:name="SettingsFragment"
            android:label="@string/settings"
            tools:layout="@layout/fragment_settings" />
    </navigation>
    

    suppose current fragment opened is the home fragment and you want to navigate to users fragment, for that just call in the setOnClickListener of the element that you want to navigate to the navigate method from the nav controller similar to this code:

    yourElement.setOnClickListener {
      view.findNavController().navigate(R.id.nav_users)
    }
    

    that will make the app navigate to that other fragment and will also handle the title in the toolbar.

    0 讨论(0)
  • 2020-12-08 00:45

    You don't really need to pop the Splash Fragment. It can remain there for the rest of your App life. What you should do is from the Splash Screen determine which next Screen to Show.

    In the picture above you can ask in the Splash Screen State if there is a saved LoginToken. In case is empty then you navigate to the Login Screen.

    Once the Login Screen is done, then you analyze the result save the Token and navigate to your Next Fragment Home Screen.

    When the Back Button is Pressed in the Home Screen, you will send back a Result message to the Splash Screen that indicates it to finish the App.

    Bellow code may help:

    val nextDestination = if (loginSuccess) {
        R.id.action_Dashboard
    } else {
        R.id.action_NotAuthorized
    }
    
    val options = NavOptions.Builder()
        .setPopUpTo(R.id.loginParentFragment, true)
        .build()
    
    findNavController().navigate(nextDestination, null, options)
    
    0 讨论(0)
  • 2020-12-08 00:47

    You can also try the followings.

    val navController = findNavController(R.id.nav_host_fragment)
    if (condition) {
        navController.setGraph(R.navigation.nav_graph_first)
    } else {
        navController.setGraph(R.navigation.nav_graph_second)
    }
    

    Instead of trying to pop start destination or navigate manually to target destination, it would be better to have another navigation graph with different workflow. This would be even better for the case when you want completely different navigation flow conditionally.

    0 讨论(0)
  • 2020-12-08 00:48

    In MainActivity.kt

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        val inflater = navHostFragment.navController.navInflater
        val graph = inflater.inflate(R.navigation.booking_navigation)
    
        if (isTrue){
            graph.startDestination = R.id.DetailsFragment
        }else {
            graph.startDestination = R.id.OtherDetailsFragment
        }
    
        val navController = navHostFragment.navController
        navController.setGraph(graph, intent.extras)
    

    Remove startDestination from nav_graph.xml

    ?xml version="1.0" encoding="utf-8"?>
    <!-- app:startDestination="@id/oneFragment" -->
    
    <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/navigation_main">
    
      <fragment
        android:id="@+id/DetailFragment"
        android:name="DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail"/>
      <fragment
        android:id="@+id/OtherDetailFragment"
        android:name="OtherDetailFragment"
        android:label="fragment_other_detail"
        tools:layout="@layout/fragment_other_detail"/>
    </navigation>
    
    0 讨论(0)
  • 2020-12-08 00:51

    Okay, after messing with this for a bit I found a solution that worked for me that didn't require a ton of work.

    It appears two things MUST be in place for it function as if your secondFragment is your start destination.

    use the ALTERNATIVE option in the accepted post

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.appname.package.FirstFragment" >
    
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:popUpTo="@+id/firstFragment"
            app:popUpToInclusive="true" /> 
    
    </fragment>
    
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.appname.package.SecondFragment"/>
    

    The above will remove firstFragment from the stack and inflate secondFragment when moving. The app cannot step back to firstFragment anymore BUT your left with secondFragment showing a back arrow as @szaske stated.

    This is what made the difference. I previously defined my AppBarConfig using the NavigationController.graph like so

    // Old code
    val controller by lazy { findNavController(R.id.nav_host_fragment) }
    val appBarConfig by lazy { AppBarConfiguration(controller.graph) }
    

    Updating it to define a set of top-level destinations rectified the issue of showing the back arrow on secondFragment instead of a hamburger menu icon.

    // secondFragment will now show hamburger menu instead of back arrow.
    val appBarConfig by lazy { AppBarConfiguration(setOf(R.id.firstFragment, R.id.secondFragment)) }
    

    Setting the start destination may or may not have negative implications in your project so do it as needed however in this example we do not need to do so. If it makes you warm and fuzzy to ensure that your graph has the correct start fragment defined, you can do it like so.

    controller.graph.startDestination = R.id.secondFragment
    

    Note: Setting this does not prevent the back arrow from occurring in secondFragment and from what I have found seems to have no effect on navigation.

    0 讨论(0)
  • 2020-12-08 00:56

    UPDATE:

    When you have nav graph like this:

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.appname.package.FirstFragment" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" /> 
    </fragment>
    
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.appname.package.SecondFragment"/>
    

    And you want to navigate to the second fragment and make it root of your graph, specify the next NavOptions:

    NavOptions navOptions = new NavOptions.Builder()
            .setPopUpTo(R.id.firstFragment, true)
            .build();
    

    And use them for the navigation:

    Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment, bundle, navOptions);
    

    setPopUpTo(int destinationId, boolean inclusive) - Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found.

    destinationId - The destination to pop up to, clearing all intervening destinations.

    inclusive - true to also pop the given destination from the back stack.


    ALTERNATIVE:

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.appname.package.FirstFragment" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment"
        app:popUpTo="@+id/firstFragment"
        app:popUpToInclusive="true" /> 
    </fragment>
    
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.appname.package.SecondFragment"/>
    

    And then on your code:

    findNavController(fragment).navigate(
        FirstFragmentDirections.actionFirstFragmentToSecondFragment())
    

    Old answer

    Deprecated: The clearTask attribute for actions and the associated API in NavOptions has been deprecated.

    Source: https://developer.android.com/jetpack/docs/release-notes


    If you want to change your root fragment to fragment 2 (e.g. after pressing back button on fragment 2 you will exit the app), you should put the next attribute to your action or destination:

    app:clearTask="true"
    

    Practically it looks in a next way:

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.appname.package.FirstFragment"
        android:label="fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:clearTask="true" /> 
    </fragment>
    
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.appname.package.SecondFragment"
        android:label="fragment_second"/>
    

    I've added app:clearTask="true" to action.


    Now when you perform navigation from fragment 1 to fragment 2 use the next code:

    Navigation.findNavController(view)
            .navigate(R.id.action_firstFragment_to_secondFragment);
    
    0 讨论(0)
提交回复
热议问题