New navigation component from arch with nested navigation graph

后端 未结 7 1071
栀梦
栀梦 2020-12-07 11:26

I have one case and wish to implement it by arch navigation component. For example I have 2 Nav Graphs (main and nested). Can I call main graph from nested and how?

7条回答
  •  有刺的猬
    2020-12-07 11:42

    I created an answer with the info devrocca provided. It's a full answer from scratch, i didn't skip anything if anyone ever needs.

    This is the main fragment for navigation. Camera is direct destination without any nested graph, Dashboard has it's own nested graph but it's added to same backstack camera fragment is added. Home has 3 fragments with it's own nav host

    MainActivity
    |- MainNavHost
       |- HomeNavHostFragment
       |  |- NestedNavHost
       |     |-HomeFragment1
       |     |-HomeFragment2
       |     |-HomeFragment3
       |  
       |- nav_graph_dashboard 
       |
       |- CameraFragment
    

    Here is the navigation files

    Main Navigation nav_graph.xml

    
    
    
        
        
    
            
            
    
            
            
    
            
            
    
        
    
        
        
    
    
        
        
    
        
        
    
    
        
        
    
    
    
    

    Dashboard nested navigation graph

    
    
    
        
            
        
    
        
        
    
    
    

    And nested navigation graph with it's own NavHost nav_graph_home

    
    
    
        
    
        
            
        
    
        
            
        
    
        
    
    
    

    Layouts, i only add necessary ones, others are simple layouts with buttons, i add link for sample project with other navigation components samples included.

    MainActivity
    
    
    
    
    
        
    
    
            
    
                
    
            
    
            
    
                
    
            
    
        
    
    
    

    Main Fragment, this is first fragment that shown in the image used as start of main navigation

    
    
    
        
    
            

    Layout that contains inner NavHostFragment for home navigation

    
    
    
        
    
            
    
        
    
    
    

    MainActivity is for checking main navigation back stack, important thing here is

    supportFragmentManager back stack is not updated as you navigate it's childFragmentManager even for main navigation, even if you only have one

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // Get NavHostFragment
            val navHostFragment =
                supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment)
    
            // ChildFragmentManager of NavHostFragment
            val navHostChildFragmentManager = navHostFragment?.childFragmentManager
    
            navHostChildFragmentManager?.addOnBackStackChangedListener {
    
                val backStackEntryCount = navHostChildFragmentManager.backStackEntryCount
                val fragments = navHostChildFragmentManager.fragments
            }
        }
    }
    

    Fragment that contains Home navigation's host

    class HomeNavHostFragment : BaseDataBindingFragment() {
        override fun getLayoutRes(): Int = R.layout.fragment_home_navhost
    
        private var navController: NavController? = null
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            val nestedNavHostFragment =
                childFragmentManager.findFragmentById(R.id.nested_nav_host_fragment) as? NavHostFragment
            navController = nestedNavHostFragment?.navController
    
            navController?.navigate(R.id.homeFragment1)
    
            listenBackStack()
        }
    
        private fun listenBackStack() {
    
            // Get NavHostFragment
            val navHostFragment =
                childFragmentManager.findFragmentById(R.id.nested_nav_host_fragment)
    
            // ChildFragmentManager of the current NavHostFragment
            val navHostChildFragmentManager = navHostFragment?.childFragmentManager
    
            navHostChildFragmentManager?.addOnBackStackChangedListener {
    
                val backStackEntryCount = navHostChildFragmentManager!!.backStackEntryCount
                val fragments = navHostChildFragmentManager!!.fragments
    
                Toast.makeText(
                    requireContext(),
                    "HomeNavHost backStackEntryCount: $backStackEntryCount, fragments: $fragments",
                    Toast.LENGTH_SHORT
                ).show()
            }
    
    
            val callback = object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
    
                    val backStackEntryCount = navHostChildFragmentManager!!.backStackEntryCount
    
                    Toast.makeText(
                        requireContext(),
                        "HomeNavHost backStackEntryCount: $backStackEntryCount",
                        Toast.LENGTH_SHORT
                    ).show()
    
    
            if (backStackEntryCount == 1) {
                    OnBackPressedCallback@ this.isEnabled = false
                    requireActivity().onBackPressed()
                } else {
                    navController?.navigateUp()
                }
            }
            }
    
            requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
    
        }
    }
    

    There is one thing i don't know if it's improved in graph or code with nested NavHostFragment

    If you set start destination of nav_graph_home HomeFragment1 instead of HomeNavHostFragment it works as dashboard which ignores nested NavHost and added to main back stack of fragments.

    Since you are in inner NavHostFragment findNavController() in any home fragment returns the inner one

    class HomeFragment3 : BaseDataBindingFragment() {
        override fun getLayoutRes(): Int = R.layout.fragment_home3
    
        private var count = 0
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            dataBinding.btnIncrease.setOnClickListener {
                dataBinding.tvTitle.text = "Count: ${count++}"
            }
    
    
            val mainNavController =
                Navigation.findNavController(requireActivity(), R.id.main_nav_host_fragment)
    
            dataBinding.btnGoToStart.setOnClickListener {
    
                // 

提交回复
热议问题